Fix replicate with destination interleave
This commit is contained in:
		
							parent
							
								
									74fa50bc0c
								
							
						
					
					
						commit
						a1a0430ec3
					
				|  | @ -75,11 +75,9 @@ pub fn TransferMenu() -> Html { | ||||||
|                     dispatch.reduce_mut(|state| { |                     dispatch.reduce_mut(|state| { | ||||||
|                         state.interleave_x = num; |                         state.interleave_x = num; | ||||||
|                     }); |                     }); | ||||||
|                     if let Some((_,y)) = ct_state.transfer.interleave_dest { |                     ct_dispatch.reduce_mut(|state| { | ||||||
|                         ct_dispatch.reduce_mut(|state| { |                         state.transfer.interleave_dest = (num, state.transfer.interleave_dest.1); | ||||||
|                             state.transfer.interleave_dest = Some((num, y)); |                     }); | ||||||
|                         }); |  | ||||||
|                     } |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }) |         }) | ||||||
|  | @ -97,11 +95,9 @@ pub fn TransferMenu() -> Html { | ||||||
|                     dispatch.reduce_mut(|state| { |                     dispatch.reduce_mut(|state| { | ||||||
|                         state.interleave_y = num; |                         state.interleave_y = num; | ||||||
|                     }); |                     }); | ||||||
|                     if let Some((x,_)) = ct_state.transfer.interleave_dest { |                     ct_dispatch.reduce_mut(|state| { | ||||||
|                         ct_dispatch.reduce_mut(|state| { |                         state.transfer.interleave_dest = (state.transfer.interleave_dest.0, num); | ||||||
|                             state.transfer.interleave_dest = Some((x, num)); |                     }); | ||||||
|                         }); |  | ||||||
|                     } |  | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
|         }) |         }) | ||||||
|  |  | ||||||
|  | @ -24,14 +24,27 @@ impl TryFrom<Region> for ((u8, u8), (u8, u8)) { | ||||||
|     } |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| #[derive(PartialEq, Eq, Default, Clone, Copy, Serialize, Deserialize, Debug)] | #[derive(PartialEq, Eq, Clone, Copy, Serialize, Deserialize, Debug)] | ||||||
| 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.
 | ||||||
|     pub dest_plate: Plate, |     pub dest_plate: Plate, | ||||||
|     pub dest_region: Region, |     pub dest_region: Region, | ||||||
|     pub interleave_source: Option<(i8, i8)>, |     pub interleave_source: (i8, i8), | ||||||
|     pub interleave_dest: Option<(i8, i8)>, |     pub interleave_dest: (i8, i8), | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | impl Default for TransferRegion { | ||||||
|  |     fn default() -> Self { | ||||||
|  |         TransferRegion { | ||||||
|  |             source_plate: Plate::default(), | ||||||
|  |             source_region: Region::default(), | ||||||
|  |             dest_plate: Plate::default(), | ||||||
|  |             dest_region: Region::default(), | ||||||
|  |             interleave_source: (1,1), | ||||||
|  |             interleave_dest: (1,1) | ||||||
|  |         } | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| impl TransferRegion { | impl TransferRegion { | ||||||
|  | @ -40,7 +53,7 @@ impl TransferRegion { | ||||||
|             Region::Rect(c1, c2) => { |             Region::Rect(c1, c2) => { | ||||||
|             let mut wells = Vec::<(u8, u8)>::new(); |             let mut wells = Vec::<(u8, u8)>::new(); | ||||||
|             let (ul, br) = standardize_rectangle(&c1, &c2); |             let (ul, br) = standardize_rectangle(&c1, &c2); | ||||||
|             let (interleave_i, interleave_j) = self.interleave_source.unwrap_or((1, 1)); |             let (interleave_i, interleave_j) = self.interleave_source; | ||||||
|             // NOTE: This will panic if either is 0!
 |             // NOTE: This will panic if either is 0!
 | ||||||
|             // We'll reassign these values (still not mutable) just in case.
 |             // We'll reassign these values (still not mutable) just in case.
 | ||||||
|             // This behaviour shouldn't be replicated for destination wells
 |             // This behaviour shouldn't be replicated for destination wells
 | ||||||
|  | @ -82,16 +95,17 @@ impl TransferRegion { | ||||||
|     pub fn calculate_map(&self) -> Box<dyn Fn((u8, u8)) -> Option<Vec<(u8, u8)>> + '_> { |     pub fn calculate_map(&self) -> Box<dyn Fn((u8, u8)) -> Option<Vec<(u8, u8)>> + '_> { | ||||||
|         // By validating first, we have a stronger guarantee that
 |         // By validating first, we have a stronger guarantee that
 | ||||||
|         // this function will not panic. :)
 |         // this function will not panic. :)
 | ||||||
|  |         log::debug!("Validating: {:?}", self.validate()); | ||||||
|         if let Err(msg) = self.validate() { |         if let Err(msg) = self.validate() { | ||||||
|             eprintln!("{}", msg); |             eprintln!("{}", msg); | ||||||
|             log::debug!("{}", msg); |  | ||||||
|             eprintln!("This transfer will be empty."); |             eprintln!("This transfer will be empty."); | ||||||
|             return Box::new(|(_, _)| None); |             return Box::new(|(_, _)| None); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         log::debug!("What is ild? {:?}", self); | ||||||
|         let source_wells = self.get_source_wells(); |         let source_wells = self.get_source_wells(); | ||||||
|         let il_dest = self.interleave_dest.unwrap_or((1, 1)); |         let il_dest = self.interleave_dest; | ||||||
|         let il_source = self.interleave_source.unwrap_or((1, 1)); |         let il_source = self.interleave_source; | ||||||
| 
 | 
 | ||||||
|         let source_corners: ((u8, u8), (u8, u8)) = match self.source_region { |         let source_corners: ((u8, u8), (u8, u8)) = match self.source_region { | ||||||
|             Region::Point((x,y)) => ((x,y),(x,y)), |             Region::Point((x,y)) => ((x,y),(x,y)), | ||||||
|  | @ -130,32 +144,56 @@ impl TransferRegion { | ||||||
|             } |             } | ||||||
|             Region::Rect(c1, c2) => { |             Region::Rect(c1, c2) => { | ||||||
|                 return Box::new(move |(i, j)| { |                 return Box::new(move |(i, j)| { | ||||||
|                     // Because of our call to validate,
 |  | ||||||
|                     // we can assume that our destination region contains
 |  | ||||||
|                     // an integer number of our source regions.
 |  | ||||||
|                     if source_wells.contains(&(i, j)) { |                     if source_wells.contains(&(i, j)) { | ||||||
|                         // Find points by checking congruence class
 |  | ||||||
|                         let possible_destination_wells = create_dense_rectangle(&c1, &c2); |                         let possible_destination_wells = create_dense_rectangle(&c1, &c2); | ||||||
|                         let (ds1, _) = standardize_rectangle(&c1, &c2); |                         let (d_ul, d_br) = standardize_rectangle(&c1, &c2); | ||||||
|                         let (s1, s2) = standardize_rectangle(&source_corners.0, &source_corners.1); |                         let (s_ul, s_br) = standardize_rectangle(&source_corners.0, &source_corners.1); | ||||||
|                         let dims = ( |                         let s_dims = ( | ||||||
|                             s2.0.checked_sub(s1.0).unwrap() + 1, |                             s_br.0.checked_sub(s_ul.0).unwrap() + 1, | ||||||
|                             s2.1.checked_sub(s1.1).unwrap() + 1, |                             s_br.1.checked_sub(s_ul.1).unwrap() + 1, | ||||||
|  |                         ); | ||||||
|  |                         let d_dims = ( | ||||||
|  |                             d_br.0.checked_sub(d_ul.0).unwrap() + 1, | ||||||
|  |                             d_br.1.checked_sub(d_ul.1).unwrap() + 1, | ||||||
|  |                         ); | ||||||
|  |                         let N_s = ( // Number of used source wells
 | ||||||
|  |                             s_dims.0.div_euclid(il_source.0.abs() as u8), | ||||||
|  |                             s_dims.1.div_euclid(il_source.1.abs() as u8), | ||||||
|  |                         ); | ||||||
|  |                         let D_per_replicate = ( // How many wells are used per replicate?
 | ||||||
|  |                             (s_dims.0 * (il_dest.0.abs() as u8)) | ||||||
|  |                             // Conditionally subtract one to ignore the trailing interleave well
 | ||||||
|  |                             .saturating_sub(if il_dest.0.abs() > 1 {1} else {0}), | ||||||
|  |                             (s_dims.1 * (il_dest.1.abs() as u8)) | ||||||
|  |                             .saturating_sub(if il_dest.1.abs() > 1 {1} else {0}), | ||||||
|  |                         ); | ||||||
|  |                         let count = ( // How many times can we replicate?
 | ||||||
|  |                             d_dims.0.div_euclid(D_per_replicate.0), | ||||||
|  |                             d_dims.1.div_euclid(D_per_replicate.1), | ||||||
|                         ); |                         ); | ||||||
| 
 | 
 | ||||||
|                         Some( |                         Some( | ||||||
|                             possible_destination_wells |                             possible_destination_wells | ||||||
|                                 .into_iter() |                                 .into_iter() | ||||||
|                                 .filter(|(x, y)| { |                                 .filter(|(x, _)| { | ||||||
|                                     i * il_dest.0.abs() as u8 % dims.0 |                                     x.checked_sub(d_ul.0).unwrap() | ||||||
|                                         == (x.checked_sub(ds1.0).unwrap() + 1) % dims.0 |                                         % (N_s.0 * il_dest.0.abs() as u8) // Counter along x
 | ||||||
|                                         && j * il_dest.1.abs() as u8 % dims.1 |                                     == ((il_dest.0.abs() as u8 *(i).checked_sub(1u8).unwrap())) | ||||||
|                                             == (y.checked_sub(ds1.1).unwrap() + 1) % dims.1 |                                         % (N_s.0 * il_dest.0.abs() as u8) | ||||||
|                                 }) |                                 }) | ||||||
|                                 .filter(|(x, y)| { |                                 .filter(|(_, y)| { | ||||||
|                                     (x.checked_sub(ds1.0).unwrap()) % il_dest.0.abs() as u8 == 0 |                                     y.checked_sub(d_ul.1).unwrap() | ||||||
|                                         && (y.checked_sub(ds1.1).unwrap()) % il_dest.1.abs() as u8 |                                         % (N_s.1 * il_dest.1.abs() as u8) // Counter along u
 | ||||||
|                                             == 0 |                                     == ((il_dest.1.abs() as u8 *(j).checked_sub(1u8).unwrap())) | ||||||
|  |                                         % (N_s.1 * il_dest.1.abs() as u8) | ||||||
|  |                                 }) | ||||||
|  |                                 .filter(|(x,y)| { | ||||||
|  |                                     // How many times have we replicated? < How many are we allowed
 | ||||||
|  |                                     // to replicate?
 | ||||||
|  |                                     x.checked_sub(d_ul.0).unwrap().div_euclid(N_s.0 * il_dest.0.abs() as u8) 
 | ||||||
|  |                                         < count.0 && | ||||||
|  |                                     y.checked_sub(d_ul.1).unwrap().div_euclid(N_s.1 * il_dest.1.abs() as u8) | ||||||
|  |                                         < count.1 | ||||||
|                                 }) |                                 }) | ||||||
|                                 .collect(), |                                 .collect(), | ||||||
|                         ) |                         ) | ||||||
|  | @ -176,8 +214,8 @@ impl TransferRegion { | ||||||
|         //     - Are the wells in the source really there?
 |         //     - Are the wells in the source really there?
 | ||||||
|         //     - In a replication region, do the source lengths divide the destination lengths?
 |         //     - In a replication region, do the source lengths divide the destination lengths?
 | ||||||
|         //     - Are the interleaves valid?
 |         //     - Are the interleaves valid?
 | ||||||
|         let il_source = self.interleave_source.unwrap_or((1, 1)); |         let il_source = self.interleave_source; | ||||||
|         let il_dest = self.interleave_dest.unwrap_or((1, 1)); |         let il_dest = self.interleave_dest; | ||||||
| 
 | 
 | ||||||
|         match self.source_region { |         match self.source_region { | ||||||
|             Region::Point(_) => return Ok(()), // Should make sure it's actually in the plate, leave for
 |             Region::Point(_) => return Ok(()), // Should make sure it's actually in the plate, leave for
 | ||||||
|  | @ -233,10 +271,8 @@ impl TransferRegion { | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if let Some(source_il) = self.interleave_source { |         if il_source.0 == 0 || il_dest.1 == 0 { | ||||||
|             if source_il.0 == 0 || source_il.1 == 0 { |             return Err("Source interleave cannot be zero!"); | ||||||
|                 return Err("Source interleave cannot be zero!"); |  | ||||||
|             } |  | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         // Check if all destination wells exist:
 |         // Check if all destination wells exist:
 | ||||||
|  | @ -341,8 +377,8 @@ mod tests { | ||||||
|             source_region: Region::Rect((1, 1), (3, 3)), |             source_region: Region::Rect((1, 1), (3, 3)), | ||||||
|             dest_plate: destination, |             dest_plate: destination, | ||||||
|             dest_region: Region::Point((3,3)), |             dest_region: Region::Point((3,3)), | ||||||
|             interleave_source: None, |             interleave_source: (1,1), | ||||||
|             interleave_dest: None, |             interleave_dest: (1,1), | ||||||
|         }; |         }; | ||||||
|         let transfer1_map = transfer1.calculate_map(); |         let transfer1_map = transfer1.calculate_map(); | ||||||
|         assert_eq!(transfer1_map((1,1)), Some(vec!{(3,3)}), "Failed basic shift transfer 1"); |         assert_eq!(transfer1_map((1,1)), Some(vec!{(3,3)}), "Failed basic shift transfer 1"); | ||||||
|  | @ -354,8 +390,8 @@ mod tests { | ||||||
|             source_region: Region::Rect((1, 1), (3, 3)), |             source_region: Region::Rect((1, 1), (3, 3)), | ||||||
|             dest_plate: destination, |             dest_plate: destination, | ||||||
|             dest_region: Region::Point((3,3)), |             dest_region: Region::Point((3,3)), | ||||||
|             interleave_source: Some((2,2)), |             interleave_source: (2,2), | ||||||
|             interleave_dest: None, |             interleave_dest: (1,1), | ||||||
|         }; 
 |         }; 
 | ||||||
|         let transfer2_map = transfer2.calculate_map(); |         let transfer2_map = transfer2.calculate_map(); | ||||||
|         assert_eq!(transfer2_map((1,1)), Some(vec!{(3,3)}), "Failed source interleave, type simple 1"); |         assert_eq!(transfer2_map((1,1)), Some(vec!{(3,3)}), "Failed source interleave, type simple 1"); | ||||||
|  | @ -368,8 +404,8 @@ mod tests { | ||||||
|             source_region: Region::Rect((1, 1), (3, 3)), |             source_region: Region::Rect((1, 1), (3, 3)), | ||||||
|             dest_plate: destination, |             dest_plate: destination, | ||||||
|             dest_region: Region::Point((3,3)), |             dest_region: Region::Point((3,3)), | ||||||
|             interleave_source: None, |             interleave_source: (1,1), | ||||||
|             interleave_dest: Some((2,3)), |             interleave_dest: (2,3), | ||||||
|         }; 
 |         }; 
 | ||||||
|         let transfer3_map = transfer3.calculate_map(); |         let transfer3_map = transfer3.calculate_map(); | ||||||
|         assert_eq!(transfer3_map((1,1)), Some(vec!{(3,3)}), "Failed destination interleave, type simple 1"); |         assert_eq!(transfer3_map((1,1)), Some(vec!{(3,3)}), "Failed destination interleave, type simple 1"); | ||||||
|  | @ -388,8 +424,8 @@ mod tests { | ||||||
|             source_region: Region::Rect((1, 1), (2, 2)), |             source_region: Region::Rect((1, 1), (2, 2)), | ||||||
|             dest_plate: destination, |             dest_plate: destination, | ||||||
|             dest_region: Region::Rect((2,2),(11,11)), |             dest_region: Region::Rect((2,2),(11,11)), | ||||||
|             interleave_source: None, |             interleave_source: (1,1), | ||||||
|             interleave_dest: Some((3,3)), |             interleave_dest: (3,3), | ||||||
|         }; |         }; | ||||||
|         let transfer1_map = transfer1.calculate_map(); |         let transfer1_map = transfer1.calculate_map(); | ||||||
|         assert_eq!(transfer1_map((1,1)), Some(vec!{(2, 2), (2, 8), (8, 2), (8, 8)}), "Failed type replicate 1"); |         assert_eq!(transfer1_map((1,1)), Some(vec!{(2, 2), (2, 8), (8, 2), (8, 8)}), "Failed type replicate 1"); | ||||||
|  |  | ||||||
|  | @ -25,8 +25,8 @@ pub fn plate_test() { | ||||||
|         source_region: transfer_region::Region::Rect((1, 1), (2, 2)), |         source_region: transfer_region::Region::Rect((1, 1), (2, 2)), | ||||||
|         dest_plate: destination, |         dest_plate: destination, | ||||||
|         dest_region: transfer_region::Region::Rect((2,2),(11,11)), |         dest_region: transfer_region::Region::Rect((2,2),(11,11)), | ||||||
|         interleave_source: None, |         interleave_source: (1,1), | ||||||
|         interleave_dest: Some((3,3)), |         interleave_dest: (3,3), | ||||||
|     }; |     }; | ||||||
|     println!("{}", transfer); |     println!("{}", transfer); | ||||||
|     let sws = transfer.get_source_wells(); |     let sws = transfer.get_source_wells(); | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue