From 619f9594cf297bbba0ff3872f8562557a235bd5b Mon Sep 17 00:00:00 2001 From: Emilia Date: Sat, 11 Jan 2025 09:52:25 -0500 Subject: [PATCH] lib fix: do not permit out-of-plate destinations --- plate-tool-lib/src/transfer_region.rs | 82 +++++++++++++++++++++------ 1 file changed, 65 insertions(+), 17 deletions(-) diff --git a/plate-tool-lib/src/transfer_region.rs b/plate-tool-lib/src/transfer_region.rs index ca99f5a..a6be77d 100644 --- a/plate-tool-lib/src/transfer_region.rs +++ b/plate-tool-lib/src/transfer_region.rs @@ -250,24 +250,39 @@ impl TransferRegion { // Non-replicate transfers: match &self.dest_region { - Region::Point(Well { row: x, col: y }) => { - Box::new(move |Well { row: i, col: j }| { - if source_wells.contains(&Well { row: i, col: j }) { + Region::Point(dest_point) => { + // Breaking from form somewhat, we really should return an entirely different + // function if a point-dest can't fit the whole source. + // If the bottom-right well of the source won't fit in the dest, + // we can abort. + let source_bottom_right = match self.source_region { + Region::Point(x) => x, + Region::Rect(w1, w2) => standardize_rectangle(&w1, &w2).1, + Region::Custom(_) => unreachable!("A point destination region cannot be paired with a custom source destination?"), + }; + let bottom_right_mapped = Self::point_destination_region_calc( + source_bottom_right, + source_ul, + il_source, + il_dest, + *dest_point, + ); + if bottom_right_mapped.row > self.dest_plate.plate_format.rows() + || bottom_right_mapped.col > self.dest_plate.plate_format.columns() + { + return Box::new(|_| None); + } + + Box::new(move |input| { + if source_wells.contains(&input) { // Validity here already checked by self.validate() - Some(vec![Well { - row: x + i - .checked_sub(source_ul.row) - .expect("Point cannot have been less than UL") - .checked_div(il_source.0.unsigned_abs()) - .expect("Source interleave cannot be 0") - .mul(il_dest.0.unsigned_abs()), - col: y + j - .checked_sub(source_ul.col) - .expect("Point cannot have been less than UL") - .checked_div(il_source.1.unsigned_abs()) - .expect("Source interleave cannot be 0") - .mul(il_dest.1.unsigned_abs()), - }]) + Some(vec![Self::point_destination_region_calc( + input, + source_ul, + il_source, + il_dest, + *dest_point, + )]) } else { None } @@ -387,6 +402,33 @@ impl TransferRegion { } } + fn point_destination_region_calc( + input: Well, + source_ul: Well, + il_source: (i8, i8), + il_dest: (i8, i8), + dest_point: Well, + ) -> Well { + Well { + row: dest_point.row + + input + .row + .checked_sub(source_ul.row) + .expect("Point cannot have been less than UL") + .checked_div(il_source.0.unsigned_abs()) + .expect("Source interleave cannot be 0") + .mul(il_dest.0.unsigned_abs()), + col: dest_point.col + + input + .col + .checked_sub(source_ul.col) + .expect("Point cannot have been less than UL") + .checked_div(il_source.1.unsigned_abs()) + .expect("Source interleave cannot be 0") + .mul(il_dest.1.unsigned_abs()), + } + } + pub fn validate(&self) -> Result<(), &'static str> { // Checks if the region does anything suspect // @@ -400,6 +442,12 @@ impl TransferRegion { let il_dest = self.interleave_dest; match self.source_region { + /* + * Note 04Jan2025: + * I genuinely cannot think of a reason why we should need to validate a source + * point region??? + * Like, why would it *not* be in the plate? + */ Region::Point(_) => return Ok(()), // Should make sure it's actually in the plate, leave for // later Region::Rect(s1, s2) => {