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