Useful impls on transfer_region

This commit is contained in:
Emilia Allison 2025-01-02 21:21:17 -05:00
parent 6b7d657760
commit 8bf11eecca
Signed by: emilia
GPG Key ID: 05D5D1107E5100A1
1 changed files with 112 additions and 14 deletions

View File

@ -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)
}
}
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)]
#[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(),
)
@ -319,8 +417,8 @@ impl TransferRegion {
return Err("Source region is out-of-bounds! (Too wide)");
}
if il_dest == (0,0) {
return Err("Refusing to pool both dimensions in a rectangular transfer!\nPlease select a point in the destination plate.")
if il_dest == (0, 0) {
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 {