diff --git a/app-proto/src/add_remove.rs b/app-proto/src/add_remove.rs index cd8b4f9..8bf6bb1 100644 --- a/app-proto/src/add_remove.rs +++ b/app-proto/src/add_remove.rs @@ -199,7 +199,7 @@ pub fn AddRemove() -> View { .try_into() .unwrap() ); - state.assembly.insert_new_regulator(subjects); + state.assembly.insert_new_product_regulator(subjects); state.selection.update(|sel| sel.clear()); } ) { "🔗" } diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index d006b7b..fe08c91 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -1,7 +1,7 @@ use nalgebra::{DMatrix, DVector, DVectorView, Vector3}; use rustc_hash::FxHashMap; use slab::Slab; -use std::{collections::BTreeSet, sync::atomic::{AtomicU64, Ordering}}; +use std::{collections::BTreeSet, rc::Rc, sync::atomic::{AtomicU64, Ordering}}; use sycamore::prelude::*; use web_sys::{console, wasm_bindgen::JsValue}; /* DEBUG */ @@ -132,14 +132,35 @@ impl Element { } } -#[derive(Clone, Copy)] +pub trait Regulator { + // get information + fn subjects(&self) -> Vec; + fn measurement(&self) -> ReadSignal; + fn set_point(&self) -> Signal; + + // write problem data + fn write_to_problem(&self, problem: &mut ConstraintProblem, elts: &Slab); +} + pub struct ProductRegulator { pub subjects: [ElementKey; 2], pub measurement: ReadSignal, pub set_point: Signal } -impl ProductRegulator { +impl Regulator for ProductRegulator { + fn subjects(&self) -> Vec { + self.subjects.into() + } + + fn measurement(&self) -> ReadSignal { + self.measurement + } + + fn set_point(&self) -> Signal { + self.set_point + } + fn write_to_problem(&self, problem: &mut ConstraintProblem, elts: &Slab) { self.set_point.with_untracked(|set_pt| { if let Some(val) = set_pt.value { @@ -169,7 +190,7 @@ type AssemblyMotion<'a> = Vec>; pub struct Assembly { // elements and regulators pub elements: Signal>, - pub regulators: Signal>, + pub regulators: Signal>>, // solution variety tangent space. the basis vectors are stored in // configuration matrix format, ordered according to the elements' column @@ -240,19 +261,23 @@ impl Assembly { ); } - fn insert_regulator(&self, regulator: ProductRegulator) { - let subjects = regulator.subjects; - let key = self.regulators.update(|regs| regs.insert(regulator)); - let subject_regulators = self.elements.with( - |elts| subjects.map(|subj| elts[subj].regulators) + fn insert_regulator(&self, regulator: Rc) { + let subjects = regulator.subjects(); + let key = self.regulators.update( + |regs| regs.insert(regulator) + ); + let subject_regulators: Vec<_> = self.elements.with( + |elts| subjects.into_iter().map( + |subj| elts[subj].regulators + ).collect() ); for regulators in subject_regulators { regulators.update(|regs| regs.insert(key)); } } - pub fn insert_new_regulator(self, subjects: [ElementKey; 2]) { - // create and insert a new regulator + pub fn insert_new_product_regulator(self, subjects: [ElementKey; 2]) { + // create and insert a new product regulator let measurement = self.elements.map( move |elts| { let representations = subjects.map(|subj| elts[subj].representation); @@ -264,26 +289,31 @@ impl Assembly { } ); let set_point = create_signal(SpecifiedValue::from_empty_spec()); - self.insert_regulator(ProductRegulator { + self.insert_regulator(Rc::new(ProductRegulator { subjects: subjects, measurement: measurement, set_point: set_point - }); + })); /* DEBUG */ // print an updated list of regulators console::log_1(&JsValue::from("Regulators:")); self.regulators.with(|regs| { for (_, reg) in regs.into_iter() { - console::log_5( - &JsValue::from(" "), - &JsValue::from(reg.subjects[0]), - &JsValue::from(reg.subjects[1]), - &JsValue::from(":"), - ®.set_point.with_untracked( - |set_pt| JsValue::from(set_pt.spec.as_str()) + console::log_1(&JsValue::from(format!( + " {:?}: {}", + reg.subjects(), + reg.set_point().with_untracked( + |set_pt| { + let spec = &set_pt.spec; + if spec.is_empty() { + "__".to_string() + } else { + spec.clone() + } + } ) - ); + ))); } }); diff --git a/app-proto/src/outline.rs b/app-proto/src/outline.rs index 2951e69..497677d 100644 --- a/app-proto/src/outline.rs +++ b/app-proto/src/outline.rs @@ -1,4 +1,5 @@ use itertools::Itertools; +use std::rc::Rc; use sycamore::prelude::*; use web_sys::{ KeyboardEvent, @@ -9,24 +10,32 @@ use web_sys::{ use crate::{ AppState, assembly, - assembly::{ElementKey, ProductRegulator, RegulatorKey}, + assembly::{ElementKey, Regulator, RegulatorKey}, specified::SpecifiedValue }; // an editable view of a regulator #[component(inline_props)] -fn RegulatorInput(regulator: ProductRegulator) -> View { +fn RegulatorInput(regulator: Rc) -> View { + // get the regulator's measurement and set point signals + let measurement = regulator.measurement(); + let set_point = regulator.set_point(); + + // the `valid` signal tracks whether the last entered value is a valid set + // point specification let valid = create_signal(true); + + // the `value` signal holds the current set point specification let value = create_signal( - regulator.set_point.with_untracked(|set_pt| set_pt.spec.clone()) + set_point.with_untracked(|set_pt| set_pt.spec.clone()) ); - // this closure resets the input value to the regulator's set point - // specification + // this `reset_value` closure resets the input value to the regulator's set + // point specification let reset_value = move || { batch(|| { valid.set(true); - value.set(regulator.set_point.with(|set_pt| set_pt.spec.clone())); + value.set(set_point.with(|set_pt| set_pt.spec.clone())); }) }; @@ -39,7 +48,7 @@ fn RegulatorInput(regulator: ProductRegulator) -> View { r#type="text", class=move || { if valid.get() { - regulator.set_point.with(|set_pt| { + set_point.with(|set_pt| { if set_pt.is_present() { "regulator-input constraint" } else { @@ -50,13 +59,13 @@ fn RegulatorInput(regulator: ProductRegulator) -> View { "regulator-input invalid" } }, - placeholder=regulator.measurement.with(|result| result.to_string()), + placeholder=measurement.with(|result| result.to_string()), bind:value=value, on:change=move |_| { valid.set( match SpecifiedValue::try_from(value.get_clone_untracked()) { Ok(set_pt) => { - regulator.set_point.set(set_pt); + set_point.set(set_pt); true } Err(_) => false @@ -80,11 +89,12 @@ fn RegulatorInput(regulator: ProductRegulator) -> View { fn RegulatorOutlineItem(regulator_key: RegulatorKey, element_key: ElementKey) -> View { let state = use_context::(); let assembly = &state.assembly; - let regulator = assembly.regulators.with(|regs| regs[regulator_key]); - let other_subject = if regulator.subjects[0] == element_key { - regulator.subjects[1] + let regulator = assembly.regulators.with(|regs| regs[regulator_key].clone()); + let subjects = regulator.subjects(); + let other_subject = if subjects[0] == element_key { + subjects[1] } else { - regulator.subjects[0] + subjects[0] }; let other_subject_label = assembly.elements.with(|elts| elts[other_subject].label.clone()); view! {