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