Basic tree view, and adding plate instances

It's... not a tree?
This commit is contained in:
Emilia Allison 2023-05-22 18:11:49 -04:00
parent 00f39e636a
commit ed816d3bbe
Signed by: emilia
GPG Key ID: 7A3F8997BFE894E0
10 changed files with 311 additions and 17 deletions

79
Cargo.lock generated
View File

@ -204,6 +204,19 @@ dependencies = [
"slab", "slab",
] ]
[[package]]
name = "getrandom"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi",
"wasm-bindgen",
]
[[package]] [[package]]
name = "gloo" name = "gloo"
version = "0.8.0" version = "0.8.0"
@ -520,9 +533,11 @@ dependencies = [
name = "plate-tool" name = "plate-tool"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"getrandom",
"lazy_static", "lazy_static",
"log", "log",
"regex", "regex",
"uuid",
"wasm-bindgen", "wasm-bindgen",
"wasm-logger", "wasm-logger",
"web-sys", "web-sys",
@ -530,6 +545,12 @@ dependencies = [
"yewdux", "yewdux",
] ]
[[package]]
name = "ppv-lite86"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
[[package]] [[package]]
name = "prettyplease" name = "prettyplease"
version = "0.1.25" version = "0.1.25"
@ -599,6 +620,36 @@ dependencies = [
"proc-macro2", "proc-macro2",
] ]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha",
"rand_core",
]
[[package]]
name = "rand_chacha"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88"
dependencies = [
"ppv-lite86",
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
dependencies = [
"getrandom",
]
[[package]] [[package]]
name = "regex" name = "regex"
version = "1.8.1" version = "1.8.1"
@ -799,12 +850,40 @@ version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "uuid"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "345444e32442451b267fc254ae85a209c64be56d2890e601a0c37ff0c3c5ecd2"
dependencies = [
"getrandom",
"rand",
"uuid-macro-internal",
]
[[package]]
name = "uuid-macro-internal"
version = "1.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f67b459f42af2e6e1ee213cb9da4dbd022d3320788c3fb3e1b893093f1e45da"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.16",
]
[[package]] [[package]]
name = "version_check" name = "version_check"
version = "0.9.4" version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]] [[package]]
name = "wasm-bindgen" name = "wasm-bindgen"
version = "0.2.86" version = "0.2.86"

View File

@ -9,8 +9,10 @@ edition = "2021"
yew = { version = "0.20.0", features = ["csr"] } yew = { version = "0.20.0", features = ["csr"] }
yewdux = "0.9" yewdux = "0.9"
wasm-bindgen = "0.2" wasm-bindgen = "0.2"
web-sys = "0.3" web-sys = { version = "0.3", features = ["FormData", "HtmlFormElement"] }
log = "0.4" log = "0.4"
wasm-logger = "0.2" wasm-logger = "0.2"
regex = "1" regex = "1"
lazy_static = "1.4" lazy_static = "1.4"
uuid = { version = "1.3", features = ["v4", "fast-rng", "macro-diagnostics"] }
getrandom = { version = "0.2", features = ["js"] }

View File

@ -1,5 +1,8 @@
use yewdux::prelude::*; use yewdux::prelude::*;
use super::transfer_menu::RegionDisplay; use super::transfer_menu::RegionDisplay;
use crate::data::plate_instances::PlateInstance;
use crate::data::transfer::Transfer;
use crate::data::plate::*;
#[derive(Debug, Default, Clone, PartialEq, Store)] #[derive(Debug, Default, Clone, PartialEq, Store)]
pub struct NewTransferState { pub struct NewTransferState {
@ -8,3 +11,50 @@ pub struct NewTransferState {
pub interleave_x: u8, pub interleave_x: u8,
pub interleave_y: u8, pub interleave_y: u8,
} }
#[derive(Default, PartialEq, Clone)]
pub struct MainState {
pub source_plates: Vec<PlateInstance>,
pub destination_plates: Vec<PlateInstance>,
pub transfers: Vec<Transfer>,
}
impl Store for MainState {
fn new() -> Self {
Self {
source_plates: Vec::new(),
destination_plates: Vec::new(),
transfers: Vec::new(),
}
}
fn should_notify(&self, old: &Self) -> bool {
self != old
}
}
impl MainState {
pub fn purge_transfers(&mut self) {
// Removes any transfers for which the associated plates are gone
self.transfers = self.transfers.iter()
.filter(|tr| {
self.source_plates.iter().any(|spi| {
spi.get_uuid() == tr.source_id
}) &&
self.destination_plates.iter().any(|dpi| {
dpi.get_uuid() == tr.dest_id
})
})
.map(|&tr| tr)
.collect();
}
pub fn add_source_plate(&mut self, plate: PlateInstance) {
assert!(plate.plate.plate_type == PlateType::Source);
self.source_plates.push(plate);
}
pub fn add_dest_plate(&mut self, plate: PlateInstance) {
assert!(plate.plate.plate_type == PlateType::Destination);
self.destination_plates.push(plate);
}
}

View File

@ -1,10 +1,100 @@
#![allow(non_snake_case)] #![allow(non_snake_case)]
use std::fmt::format;
use wasm_bindgen::JsCast;
use web_sys::{EventTarget, HtmlFormElement, FormData};
use yew::prelude::*; use yew::prelude::*;
use yewdux::prelude::*;
use crate::data::{plate_instances::PlateInstance, transfer::Transfer};
use crate::data::plate::*;
use crate::components::states::MainState;
#[function_component] #[function_component]
pub fn Tree() -> Html { pub fn Tree() -> Html {
let (state, dispatch) = use_store::<MainState>();
let source_plates = state.source_plates.iter()
.map(|spi| {
html!{ <li> {String::from(spi)} </li> }
}).collect::<Html>();
let dest_plates = state.destination_plates.iter()
.map(|spi| {
html!{ <li> {String::from(spi)} </li> }
}).collect::<Html>();
let new_plate_callback = {
let dispatch = dispatch.clone();
Callback::from(move |e: SubmitEvent| {
e.prevent_default();
let target: Option<EventTarget> = e.target();
let form = target.and_then(|t| t.dyn_into::<HtmlFormElement>().ok());
if let Some(form) = form {
if let Ok(form_data) = FormData::new_with_form(&form) {
let name = form_data.get("new_plate_name").as_string().unwrap();
let format = match form_data.get("plate_format").as_string().unwrap().as_str() {
"384" => PlateFormat::W384,
"96" => PlateFormat::W96,
_ => PlateFormat::W6,
};
let plate_type = match form_data.get("new_plate_type").as_string().unwrap().as_str() {
"src" => PlateType::Source,
"dest" => PlateType::Destination,
_ => PlateType::Source,
};
dispatch.reduce_mut(|s| {
if plate_type == PlateType::Source {
s.add_source_plate(PlateInstance::new(PlateType::Source, format, name))
} else {
s.add_dest_plate(PlateInstance::new(PlateType::Destination, format, name))
}
});
}
}
})
};
html! { html! {
<div class="tree"> <div class="tree">
<div id="source-plates">
<h3>{"Source Plates:"}</h3>
<ul>
{source_plates}
</ul>
</div>
<div id="destination-plates">
<h3>{"Destination Plates:"}</h3>
<ul>
{dest_plates}
</ul>
</div>
<div id="transfers">
<h3>{"Transfers:"}</h3>
<ul>
</ul>
</div>
// Temporary
<hr/>
<div>
<form onsubmit={new_plate_callback}>
<input type="text" name="new_plate_name" placeholder="Name"/>
<select name="plate_format">
<option value="96">{"96"}</option>
<option value="384">{"384"}</option>
</select>
<input type="radio" name="new_plate_type" id="npt_src" value="src" />
<label for="npt_src">{"Source"}</label>
<input type="radio" name="new_plate_type" id="npt_dest" value="dest" />
<label for="npt_dest">{"Destination"}</label>
<input type="submit" name="new_plate_button" value="Create" />
</form>
</div>
</div> </div>
} }
} }
impl From<&PlateInstance> for String {
fn from(value: &PlateInstance) -> Self {
// Could have other formatting here
format!("{}, {}", value.name, value.plate.plate_format)
}
}

View File

@ -1,2 +1,4 @@
pub mod plate; pub mod plate;
pub mod transfer_region; pub mod transfer_region;
pub mod transfer;
pub mod plate_instances;

View File

@ -1,3 +1,4 @@
#[derive(PartialEq, Eq, Clone, Copy)]
pub struct Plate { pub struct Plate {
pub plate_type: PlateType, pub plate_type: PlateType,
pub plate_format: PlateFormat, pub plate_format: PlateFormat,
@ -16,11 +17,13 @@ impl Plate {
} }
} }
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum PlateType { pub enum PlateType {
Source, Source,
Destination, Destination,
} }
#[derive(PartialEq, Eq, Clone, Copy)]
pub enum PlateFormat { pub enum PlateFormat {
W6, W6,
W12, W12,
@ -31,6 +34,20 @@ pub enum PlateFormat {
W1536, W1536,
W3456, W3456,
} }
impl std::fmt::Display for PlateFormat {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
PlateFormat::W6 => write!(f, "6"),
PlateFormat::W12 => write!(f, "12"),
PlateFormat::W24 => write!(f, "24"),
PlateFormat::W48 => write!(f, "48"),
PlateFormat::W96 => write!(f, "96"),
PlateFormat::W384 => write!(f, "384"),
PlateFormat::W1536 => write!(f, "1536"),
PlateFormat::W3456 => write!(f, "3456"),
}
}
}
impl PlateFormat { impl PlateFormat {
pub fn size(&self) -> (u8, u8) { pub fn size(&self) -> (u8, u8) {

View File

@ -0,0 +1,33 @@
use uuid::Uuid;
use super::plate::*;
#[derive(PartialEq, Clone)]
pub struct PlateInstance {
pub plate: Plate,
id: Uuid,
pub name: String,
}
impl PlateInstance {
pub fn new(sort: PlateType, format: PlateFormat, name: String) -> Self {
PlateInstance {
plate: Plate { plate_type: sort, plate_format: format },
id: Uuid::new_v4(),
name,
}
}
pub fn get_uuid(&self) -> Uuid {
self.id
}
pub fn change_name(&mut self, new_name: String) {
self.name = new_name;
}
}
impl From<Plate> for PlateInstance {
fn from(value: Plate) -> Self {
PlateInstance { plate: value, id: Uuid::new_v4(), name: "New Plate".to_string() }
}
}

20
src/data/transfer.rs Normal file
View File

@ -0,0 +1,20 @@
use uuid::Uuid;
use super::transfer_region::*;
use super::plate_instances::*;
#[derive(PartialEq, Clone, Copy)]
pub struct Transfer {
pub source_id: Uuid,
pub dest_id: Uuid,
pub transfer: TransferRegion,
}
impl Transfer {
fn new(source: PlateInstance, dest: PlateInstance, tr: TransferRegion) -> Self {
Self {
source_id: source.get_uuid(),
dest_id: dest.get_uuid(),
transfer: tr
}
}
}

View File

@ -1,6 +1,6 @@
use super::plate::Plate; use super::plate::Plate;
#[derive(Clone, Copy)] #[derive(Clone, Copy, PartialEq, Eq)]
pub enum Region { pub enum Region {
Rect((u8, u8), (u8, u8)), Rect((u8, u8), (u8, u8)),
Point((u8, u8)), Point((u8, u8)),
@ -17,16 +17,17 @@ impl TryFrom<Region> for ((u8, u8), (u8, u8)) {
} }
} }
pub struct TransferRegion<'a> { #[derive(PartialEq, Eq, Clone, Copy)]
pub source_plate: &'a Plate, pub struct TransferRegion {
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: &'a Plate, pub dest_plate: Plate,
pub dest_region: Region, pub dest_region: Region,
pub interleave_source: Option<(i8, i8)>, pub interleave_source: Option<(i8, i8)>,
pub interleave_dest: Option<(i8, i8)>, pub interleave_dest: Option<(i8, i8)>,
} }
impl TransferRegion<'_> { impl TransferRegion {
pub fn get_source_wells(&self) -> Vec<(u8, u8)> { pub fn get_source_wells(&self) -> Vec<(u8, u8)> {
if let Region::Rect(c1, c2) = self.source_region { if let Region::Rect(c1, c2) = self.source_region {
let mut wells = Vec::<(u8, u8)>::new(); let mut wells = Vec::<(u8, u8)>::new();
@ -292,7 +293,7 @@ use std::fmt;
use std::ops::Mul; use std::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 {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "Source Plate:")?; writeln!(f, "Source Plate:")?;
let source_dims = self.source_plate.size(); let source_dims = self.source_plate.size();
@ -339,9 +340,9 @@ mod tests {
let destination = Plate::new(PlateType::Destination, PlateFormat::W384); let destination = Plate::new(PlateType::Destination, PlateFormat::W384);
let transfer1 = TransferRegion { let transfer1 = TransferRegion {
source_plate: &source, source_plate: source,
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: None,
interleave_dest: None, interleave_dest: None,
@ -352,9 +353,9 @@ mod tests {
assert_eq!(transfer1_map((2,2)), Some(vec!{(4,4)}), "Failed basic shift transfer 3"); assert_eq!(transfer1_map((2,2)), Some(vec!{(4,4)}), "Failed basic shift transfer 3");
let transfer2 = TransferRegion { let transfer2 = TransferRegion {
source_plate: &source, source_plate: source,
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: Some((2,2)),
interleave_dest: None, interleave_dest: None,
@ -366,9 +367,9 @@ mod tests {
assert_eq!(transfer2_map((3,3)), Some(vec!{(4,4)}), "Failed source interleave, type simple 4"); assert_eq!(transfer2_map((3,3)), Some(vec!{(4,4)}), "Failed source interleave, type simple 4");
let transfer3 = TransferRegion { let transfer3 = TransferRegion {
source_plate: &source, source_plate: source,
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: None,
interleave_dest: Some((2,3)), interleave_dest: Some((2,3)),
@ -386,9 +387,9 @@ mod tests {
let destination = Plate::new(PlateType::Destination, PlateFormat::W384); let destination = Plate::new(PlateType::Destination, PlateFormat::W384);
let transfer1 = TransferRegion { let transfer1 = TransferRegion {
source_plate: &source, source_plate: source,
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: None,
interleave_dest: Some((3,3)), interleave_dest: Some((3,3)),

View File

@ -21,9 +21,9 @@ pub fn plate_test() {
let destination = plate::Plate::new(plate::PlateType::Destination, plate::PlateFormat::W384); let destination = plate::Plate::new(plate::PlateType::Destination, plate::PlateFormat::W384);
let transfer = transfer_region::TransferRegion { let transfer = transfer_region::TransferRegion {
source_plate: &source, source_plate: source,
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: None,
interleave_dest: Some((3,3)), interleave_dest: Some((3,3)),