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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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 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(),
|
||||||
)
|
)
|
||||||
|
@ -319,8 +417,8 @@ impl TransferRegion {
|
||||||
return Err("Source region is out-of-bounds! (Too wide)");
|
return Err("Source region is out-of-bounds! (Too wide)");
|
||||||
}
|
}
|
||||||
|
|
||||||
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