Useful impls on transfer_region
This commit is contained in:
		
							parent
							
								
									6b7d657760
								
							
						
					
					
						commit
						8bf11eecca
					
				| 
						 | 
				
			
			@ -1,9 +1,16 @@
 | 
			
		|||
use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use regex::Regex;
 | 
			
		||||
 | 
			
		||||
use super::plate::Plate;
 | 
			
		||||
use crate::plate::PlateType;
 | 
			
		||||
use crate::util;
 | 
			
		||||
use crate::Well;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
 | 
			
		||||
use std::fmt;
 | 
			
		||||
use std::sync::LazyLock;
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug, Hash)]
 | 
			
		||||
pub struct CustomRegion {
 | 
			
		||||
    src: Vec<Well>,
 | 
			
		||||
    dest: Vec<Well>,
 | 
			
		||||
| 
						 | 
				
			
			@ -15,7 +22,7 @@ impl CustomRegion {
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
 | 
			
		||||
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug, Hash)]
 | 
			
		||||
pub enum Region {
 | 
			
		||||
    Rect(Well, Well),
 | 
			
		||||
    Point(Well),
 | 
			
		||||
| 
						 | 
				
			
			@ -26,6 +33,28 @@ impl Default for Region {
 | 
			
		|||
        Region::Point(Well { row: 1, col: 1 })
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl Display for Region {
 | 
			
		||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
			
		||||
        match self {
 | 
			
		||||
            Region::Rect(ul, br) => {
 | 
			
		||||
                write!(
 | 
			
		||||
                    f,
 | 
			
		||||
                    "{}{}:{}{}",
 | 
			
		||||
                    util::num_to_letters(ul.col).unwrap(),
 | 
			
		||||
                    ul.row,
 | 
			
		||||
                    util::num_to_letters(br.col).unwrap(),
 | 
			
		||||
                    br.row
 | 
			
		||||
                )
 | 
			
		||||
            }
 | 
			
		||||
            Region::Point(w) => {
 | 
			
		||||
                write!(f, "{}{}", util::num_to_letters(w.col).unwrap(), w.row)
 | 
			
		||||
            }
 | 
			
		||||
            Region::Custom(..) => {
 | 
			
		||||
                write!(f, "Custom Region")
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
impl TryFrom<Region> for (Well, Well) {
 | 
			
		||||
    type Error = &'static str;
 | 
			
		||||
    fn try_from(region: Region) -> Result<Self, Self::Error> {
 | 
			
		||||
| 
						 | 
				
			
			@ -53,9 +82,76 @@ impl Region {
 | 
			
		|||
            dest: dest_pts,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn new_from_wells(w1: Well, w2: Option<Well>) -> Self {
 | 
			
		||||
        if w2.is_none() {
 | 
			
		||||
            Self::Point(w1)
 | 
			
		||||
        } else {
 | 
			
		||||
            let w2 = w2.unwrap();
 | 
			
		||||
            let (w1, w2) = standardize_rectangle(&w1, &w2);
 | 
			
		||||
            Self::Rect(w1, w2)
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Debug)]
 | 
			
		||||
    pub fn try_parse_str(input: &str) -> Option<Self> {
 | 
			
		||||
        static POINT_REGEX: LazyLock<Regex> =
 | 
			
		||||
            LazyLock::new(|| Regex::new(r"([A-Z,a-z]+)(\d+)").unwrap());
 | 
			
		||||
        static RECT_REGEX: LazyLock<Regex> =
 | 
			
		||||
            LazyLock::new(|| Regex::new(r"([A-Z,a-z]+)(\d+):([A-Z,a-z]+)(\d+)").unwrap());
 | 
			
		||||
 | 
			
		||||
        log::info!("{:?}", input);
 | 
			
		||||
        if let Some(captures) = RECT_REGEX.captures(input) {
 | 
			
		||||
            if let (Some(row1), Some(col1), Some(row2), Some(col2)) = (
 | 
			
		||||
                crate::util::letters_to_num(&captures[1]),
 | 
			
		||||
                captures[2].parse::<u8>().ok(),
 | 
			
		||||
                crate::util::letters_to_num(&captures[3]),
 | 
			
		||||
                captures[4].parse::<u8>().ok(),
 | 
			
		||||
            ) {
 | 
			
		||||
                return Some(Region::Rect(
 | 
			
		||||
                    Well {
 | 
			
		||||
                        row: row1,
 | 
			
		||||
                        col: col1,
 | 
			
		||||
                    },
 | 
			
		||||
                    Well {
 | 
			
		||||
                        row: row2,
 | 
			
		||||
                        col: col2,
 | 
			
		||||
                    },
 | 
			
		||||
                ));
 | 
			
		||||
            } else {
 | 
			
		||||
                return None;
 | 
			
		||||
            }
 | 
			
		||||
        } else if let Some(captures) = POINT_REGEX.captures(input) {
 | 
			
		||||
            if let (Some(row), Some(col)) = (
 | 
			
		||||
                crate::util::letters_to_num(&captures[1]),
 | 
			
		||||
                captures[2].parse::<u8>().ok(),
 | 
			
		||||
            ) {
 | 
			
		||||
                return Some(Region::Point(Well { row, col }));
 | 
			
		||||
            } else {
 | 
			
		||||
                return None;
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            None
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn contains_well(&self, w: &Well, pt: Option<PlateType>) -> bool {
 | 
			
		||||
        match self {
 | 
			
		||||
            Self::Point(x) => x == w,
 | 
			
		||||
            Self::Rect(ul, br) => {
 | 
			
		||||
                w.row <= br.row && w.row >= ul.row && w.col <= br.col && w.col >= ul.col
 | 
			
		||||
            }
 | 
			
		||||
            Self::Custom(xs) => match pt {
 | 
			
		||||
                Some(PlateType::Source) => xs.src.contains(w),
 | 
			
		||||
                Some(PlateType::Destination) => xs.dest.contains(w),
 | 
			
		||||
                None => {
 | 
			
		||||
                    unreachable!("Cannot check if custom contains well without knowing the type")
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(PartialEq, Eq, Clone, Serialize, Deserialize, Debug, Hash)]
 | 
			
		||||
pub struct TransferRegion {
 | 
			
		||||
    pub source_plate: Plate,
 | 
			
		||||
    pub source_region: Region, // Even if it is just a point, we don't want corners.
 | 
			
		||||
| 
						 | 
				
			
			@ -233,8 +329,10 @@ impl TransferRegion {
 | 
			
		|||
                        let j = j
 | 
			
		||||
                            .saturating_sub(s_ul.col)
 | 
			
		||||
                            .saturating_div(il_source.1.unsigned_abs());
 | 
			
		||||
                        let checked_il_dest = (u8::max(il_dest.0.unsigned_abs(), 1u8),
 | 
			
		||||
                                                u8::max(il_dest.1.unsigned_abs(), 1u8));
 | 
			
		||||
                        let checked_il_dest = (
 | 
			
		||||
                            u8::max(il_dest.0.unsigned_abs(), 1u8),
 | 
			
		||||
                            u8::max(il_dest.1.unsigned_abs(), 1u8),
 | 
			
		||||
                        );
 | 
			
		||||
                        let row_modulus = number_used_src_wells.0 * checked_il_dest.0;
 | 
			
		||||
                        let column_modulus = number_used_src_wells.1 * checked_il_dest.1;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -256,12 +354,12 @@ impl TransferRegion {
 | 
			
		|||
                                .filter(|Well { row: x, col: y }| {
 | 
			
		||||
                                    // How many times have we replicated? < How many are we allowed
 | 
			
		||||
                                    // to replicate?
 | 
			
		||||
                                    x.checked_sub(d_ul.row).unwrap().div_euclid(
 | 
			
		||||
                                        row_modulus
 | 
			
		||||
                                    ) < count.0
 | 
			
		||||
                                        && y.checked_sub(d_ul.col).unwrap().div_euclid(
 | 
			
		||||
                                            column_modulus
 | 
			
		||||
                                        ) < count.1
 | 
			
		||||
                                    x.checked_sub(d_ul.row).unwrap().div_euclid(row_modulus)
 | 
			
		||||
                                        < count.0
 | 
			
		||||
                                        && y.checked_sub(d_ul.col)
 | 
			
		||||
                                            .unwrap()
 | 
			
		||||
                                            .div_euclid(column_modulus)
 | 
			
		||||
                                            < count.1
 | 
			
		||||
                                })
 | 
			
		||||
                                .collect(),
 | 
			
		||||
                        )
 | 
			
		||||
| 
						 | 
				
			
			@ -320,7 +418,7 @@ impl TransferRegion {
 | 
			
		|||
                }
 | 
			
		||||
 | 
			
		||||
                if il_dest == (0, 0) {
 | 
			
		||||
                    return Err("Refusing to pool both dimensions in a rectangular transfer!\nPlease select a point in the destination plate.")
 | 
			
		||||
                    return Err("Refusing to pool both dimensions in a rectangular transfer!\nPlease select a point in the destination plate.");
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            Region::Custom(_) => return Ok(()),
 | 
			
		||||
| 
						 | 
				
			
			@ -372,7 +470,7 @@ fn standardize_rectangle(c1: &Well, c2: &Well) -> (Well, Well) {
 | 
			
		|||
 | 
			
		||||
#[cfg(debug_assertions)]
 | 
			
		||||
use std::fmt;
 | 
			
		||||
use std::ops::Mul;
 | 
			
		||||
use std::{fmt::Display, ops::Mul};
 | 
			
		||||
 | 
			
		||||
#[cfg(debug_assertions)] // There should be no reason to print a transfer otherwise
 | 
			
		||||
impl fmt::Display for TransferRegion {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue