Useful impls on transfer_region
This commit is contained in:
		
							parent
							
								
									6b7d657760
								
							
						
					
					
						commit
						8bf11eecca
					
				| 
						 | 
					@ -1,9 +1,16 @@
 | 
				
			||||||
use serde::{Deserialize, Serialize};
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					use regex::Regex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use super::plate::Plate;
 | 
					use super::plate::Plate;
 | 
				
			||||||
 | 
					use crate::plate::PlateType;
 | 
				
			||||||
 | 
					use crate::util;
 | 
				
			||||||
use crate::Well;
 | 
					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 {
 | 
					pub struct CustomRegion {
 | 
				
			||||||
    src: Vec<Well>,
 | 
					    src: Vec<Well>,
 | 
				
			||||||
    dest: 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 {
 | 
					pub enum Region {
 | 
				
			||||||
    Rect(Well, Well),
 | 
					    Rect(Well, Well),
 | 
				
			||||||
    Point(Well),
 | 
					    Point(Well),
 | 
				
			||||||
| 
						 | 
					@ -26,6 +33,28 @@ impl Default for Region {
 | 
				
			||||||
        Region::Point(Well { row: 1, col: 1 })
 | 
					        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) {
 | 
					impl TryFrom<Region> for (Well, Well) {
 | 
				
			||||||
    type Error = &'static str;
 | 
					    type Error = &'static str;
 | 
				
			||||||
    fn try_from(region: Region) -> Result<Self, Self::Error> {
 | 
					    fn try_from(region: Region) -> Result<Self, Self::Error> {
 | 
				
			||||||
| 
						 | 
					@ -53,9 +82,76 @@ impl Region {
 | 
				
			||||||
            dest: dest_pts,
 | 
					            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 struct TransferRegion {
 | 
				
			||||||
    pub source_plate: Plate,
 | 
					    pub source_plate: Plate,
 | 
				
			||||||
    pub source_region: Region, // Even if it is just a point, we don't want corners.
 | 
					    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
 | 
					                        let j = j
 | 
				
			||||||
                            .saturating_sub(s_ul.col)
 | 
					                            .saturating_sub(s_ul.col)
 | 
				
			||||||
                            .saturating_div(il_source.1.unsigned_abs());
 | 
					                            .saturating_div(il_source.1.unsigned_abs());
 | 
				
			||||||
                        let checked_il_dest = (u8::max(il_dest.0.unsigned_abs(), 1u8),
 | 
					                        let checked_il_dest = (
 | 
				
			||||||
                                                u8::max(il_dest.1.unsigned_abs(), 1u8));
 | 
					                            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 row_modulus = number_used_src_wells.0 * checked_il_dest.0;
 | 
				
			||||||
                        let column_modulus = number_used_src_wells.1 * checked_il_dest.1;
 | 
					                        let column_modulus = number_used_src_wells.1 * checked_il_dest.1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -256,12 +354,12 @@ impl TransferRegion {
 | 
				
			||||||
                                .filter(|Well { row: x, col: y }| {
 | 
					                                .filter(|Well { row: x, col: y }| {
 | 
				
			||||||
                                    // How many times have we replicated? < How many are we allowed
 | 
					                                    // How many times have we replicated? < How many are we allowed
 | 
				
			||||||
                                    // to replicate?
 | 
					                                    // to replicate?
 | 
				
			||||||
                                    x.checked_sub(d_ul.row).unwrap().div_euclid(
 | 
					                                    x.checked_sub(d_ul.row).unwrap().div_euclid(row_modulus)
 | 
				
			||||||
                                        row_modulus
 | 
					                                        < count.0
 | 
				
			||||||
                                    ) < count.0
 | 
					                                        && y.checked_sub(d_ul.col)
 | 
				
			||||||
                                        && y.checked_sub(d_ul.col).unwrap().div_euclid(
 | 
					                                            .unwrap()
 | 
				
			||||||
                                            column_modulus
 | 
					                                            .div_euclid(column_modulus)
 | 
				
			||||||
                                        ) < count.1
 | 
					                                            < count.1
 | 
				
			||||||
                                })
 | 
					                                })
 | 
				
			||||||
                                .collect(),
 | 
					                                .collect(),
 | 
				
			||||||
                        )
 | 
					                        )
 | 
				
			||||||
| 
						 | 
					@ -320,7 +418,7 @@ impl TransferRegion {
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                if il_dest == (0, 0) {
 | 
					                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(()),
 | 
					            Region::Custom(_) => return Ok(()),
 | 
				
			||||||
| 
						 | 
					@ -372,7 +470,7 @@ fn standardize_rectangle(c1: &Well, c2: &Well) -> (Well, Well) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#[cfg(debug_assertions)]
 | 
					#[cfg(debug_assertions)]
 | 
				
			||||||
use std::fmt;
 | 
					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
 | 
					#[cfg(debug_assertions)] // There should be no reason to print a transfer otherwise
 | 
				
			||||||
impl fmt::Display for TransferRegion {
 | 
					impl fmt::Display for TransferRegion {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue