From ee8a01b9cb7e27a697baeb4211ed50936c1b9ebe Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Thu, 17 Apr 2025 13:20:50 -0700 Subject: [PATCH 1/4] Let regulators handle their own activation This improves code organization at the cost of a little redundancy: the default implementation of `activate` doesn't do anything, and its implementation for `HalfCurvatureRegulator` redundantly accesses the set point signal and checks whether the regulator is set. --- app-proto/src/assembly.rs | 58 ++++++++++++++++++--------------------- 1 file changed, 27 insertions(+), 31 deletions(-) diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index 055b9a2..aedc344 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -145,6 +145,8 @@ pub trait Regulator: ProblemPoser + OutlineItem { fn subjects(&self) -> Vec; fn measurement(&self) -> ReadSignal; fn set_point(&self) -> Signal; + + fn activate(&self, _assembly: &Assembly) {} } pub struct ProductRegulator { @@ -202,6 +204,17 @@ impl Regulator for HalfCurvatureRegulator { fn set_point(&self) -> Signal { self.set_point } + + fn activate(&self, assembly: &Assembly) { + if let Some(half_curv) = self.set_point.with_untracked(|set_pt| set_pt.value) { + let representation = assembly.elements.with_untracked( + |elts| elts[self.subject].representation + ); + representation.update( + |rep| change_half_curvature(rep, half_curv) + ); + } + } } impl ProblemPoser for HalfCurvatureRegulator { @@ -313,7 +326,7 @@ impl Assembly { fn insert_regulator(&self, regulator: Rc) { let subjects = regulator.subjects(); let key = self.regulators.update( - |regs| regs.insert(regulator) + |regs| regs.insert(regulator.clone()) ); let subject_regulators: Vec<_> = self.elements.with_untracked( |elts| subjects.into_iter().map( @@ -324,6 +337,19 @@ impl Assembly { regulators.update(|regs| regs.insert(key)); } + // update the realization when the regulator becomes a constraint, or is + // edited while acting as a constraint + let self_for_effect = self.clone(); + create_effect(move || { + console::log_1(&JsValue::from( + format!("Updated regulator with subjects {:?}", regulator.subjects()) + )); + if regulator.set_point().with(|set_pt| set_pt.is_present()) { + regulator.activate(&self_for_effect); + self_for_effect.realize(); + } + }); + /* DEBUG */ // print an updated list of regulators console::log_1(&JsValue::from("Regulators:")); @@ -365,18 +391,6 @@ impl Assembly { measurement: measurement, set_point: set_point })); - - // update the realization when the regulator becomes a constraint, or is - // edited while acting as a constraint - let self_for_effect = self.clone(); - create_effect(move || { - console::log_1(&JsValue::from( - format!("Updated regulator with subjects {:?}", subjects) - )); - if set_point.with(|set_pt| set_pt.is_present()) { - self_for_effect.realize(); - } - }); } pub fn insert_new_half_curvature_regulator(&self, subject: ElementKey) { @@ -390,24 +404,6 @@ impl Assembly { measurement: measurement, set_point: set_point })); - - // update the realization when the regulator becomes a constraint, or is - // edited while acting as a constraint - let self_for_effect = self.clone(); - create_effect(move || { - console::log_1(&JsValue::from( - format!("Updated regulator with subjects [{}]", subject) - )); - if let Some(half_curv) = set_point.with(|set_pt| set_pt.value) { - let representation = self_for_effect.elements.with_untracked( - |elts| elts[subject].representation - ); - representation.update( - |rep| change_half_curvature(rep, half_curv) - ); - self_for_effect.realize(); - } - }); } // --- realization --- From 8f8e806d123b1d38ec37b6e09547b2ef8323b2b4 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Thu, 17 Apr 2025 14:02:37 -0700 Subject: [PATCH 2/4] Move pointer creation into `insert_regulator` This will make it easier to give each regulator a constructor instead of an "insert new" method. --- app-proto/src/assembly.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index aedc344..be52461 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -323,11 +323,15 @@ impl Assembly { ); } - fn insert_regulator(&self, regulator: Rc) { - let subjects = regulator.subjects(); + fn insert_regulator(&self, regulator: T) { + // add the regulator to the assembly's regulator list + let regulator_rc = Rc::new(regulator); let key = self.regulators.update( - |regs| regs.insert(regulator.clone()) + |regs| regs.insert(regulator_rc.clone()) ); + + // add the regulator to each subject's regulator list + let subjects = regulator_rc.subjects(); let subject_regulators: Vec<_> = self.elements.with_untracked( |elts| subjects.into_iter().map( |subj| elts[subj].regulators @@ -342,10 +346,10 @@ impl Assembly { let self_for_effect = self.clone(); create_effect(move || { console::log_1(&JsValue::from( - format!("Updated regulator with subjects {:?}", regulator.subjects()) + format!("Updated regulator with subjects {:?}", regulator_rc.subjects()) )); - if regulator.set_point().with(|set_pt| set_pt.is_present()) { - regulator.activate(&self_for_effect); + if regulator_rc.set_point().with(|set_pt| set_pt.is_present()) { + regulator_rc.activate(&self_for_effect); self_for_effect.realize(); } }); @@ -386,11 +390,11 @@ impl Assembly { } ); let set_point = create_signal(SpecifiedValue::from_empty_spec()); - self.insert_regulator(Rc::new(ProductRegulator { + self.insert_regulator(ProductRegulator { subjects: subjects, measurement: measurement, set_point: set_point - })); + }); } pub fn insert_new_half_curvature_regulator(&self, subject: ElementKey) { @@ -399,11 +403,11 @@ impl Assembly { move |elts| elts[subject].representation.with(|rep| rep[3]) ); let set_point = create_signal(SpecifiedValue::from_empty_spec()); - self.insert_regulator(Rc::new(HalfCurvatureRegulator { + self.insert_regulator(HalfCurvatureRegulator { subject: subject, measurement: measurement, set_point: set_point - })); + }); } // --- realization --- From 52d99755f972d8a6fb5689f0a8e022687162e9fd Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Thu, 17 Apr 2025 14:10:07 -0700 Subject: [PATCH 3/4] Give each regulator a constructor The assembly shouldn't have to know how to construct regulators. --- app-proto/src/add_remove.rs | 6 ++- app-proto/src/assembly.rs | 77 ++++++++++++++++++++----------------- 2 files changed, 46 insertions(+), 37 deletions(-) diff --git a/app-proto/src/add_remove.rs b/app-proto/src/add_remove.rs index 4972d3c..deac2bb 100644 --- a/app-proto/src/add_remove.rs +++ b/app-proto/src/add_remove.rs @@ -4,7 +4,7 @@ use web_sys::{console, wasm_bindgen::JsValue}; use crate::{ engine, AppState, - assembly::{Assembly, Element} + assembly::{Assembly, Element, ProductRegulator} }; /* DEBUG */ @@ -189,7 +189,9 @@ pub fn AddRemove() -> View { .try_into() .unwrap() ); - state.assembly.insert_new_product_regulator(subjects); + state.assembly.insert_regulator( + ProductRegulator::new(subjects, &state.assembly) + ); state.selection.update(|sel| sel.clear()); } ) { "🔗" } diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index be52461..0eb3e64 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -155,6 +155,29 @@ pub struct ProductRegulator { pub set_point: Signal } +impl ProductRegulator { + pub fn new(subjects: [ElementKey; 2], assembly: &Assembly) -> ProductRegulator { + let measurement = assembly.elements.map( + move |elts| { + let representations = subjects.map(|subj| elts[subj].representation); + representations[0].with(|rep_0| + representations[1].with(|rep_1| + rep_0.dot(&(&*Q * rep_1)) + ) + ) + } + ); + + let set_point = create_signal(SpecifiedValue::from_empty_spec()); + + ProductRegulator { + subjects: subjects, + measurement: measurement, + set_point: set_point + } + } +} + impl Regulator for ProductRegulator { fn subjects(&self) -> Vec { self.subjects.into() @@ -192,6 +215,23 @@ pub struct HalfCurvatureRegulator { pub set_point: Signal } +impl HalfCurvatureRegulator { + pub fn new(subject: ElementKey, assembly: &Assembly) -> HalfCurvatureRegulator { + const CURVATURE_COMPONENT: usize = 3; + let measurement = assembly.elements.map( + move |elts| elts[subject].representation.with(|rep| rep[CURVATURE_COMPONENT]) + ); + + let set_point = create_signal(SpecifiedValue::from_empty_spec()); + + HalfCurvatureRegulator { + subject: subject, + measurement: measurement, + set_point: set_point + } + } +} + impl Regulator for HalfCurvatureRegulator { fn subjects(&self) -> Vec { vec![self.subject] @@ -285,7 +325,7 @@ impl Assembly { self.elements_by_id.update(|elts_by_id| elts_by_id.insert(id, key)); // regulate the sphere's curvature - self.insert_new_half_curvature_regulator(key); + self.insert_regulator(HalfCurvatureRegulator::new(key, &self)); key } @@ -323,7 +363,7 @@ impl Assembly { ); } - fn insert_regulator(&self, regulator: T) { + pub fn insert_regulator(&self, regulator: T) { // add the regulator to the assembly's regulator list let regulator_rc = Rc::new(regulator); let key = self.regulators.update( @@ -377,39 +417,6 @@ impl Assembly { }); } - 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); - representations[0].with(|rep_0| - representations[1].with(|rep_1| - rep_0.dot(&(&*Q * rep_1)) - ) - ) - } - ); - let set_point = create_signal(SpecifiedValue::from_empty_spec()); - self.insert_regulator(ProductRegulator { - subjects: subjects, - measurement: measurement, - set_point: set_point - }); - } - - pub fn insert_new_half_curvature_regulator(&self, subject: ElementKey) { - // create and insert a new half-curvature regulator - let measurement = self.elements.map( - move |elts| elts[subject].representation.with(|rep| rep[3]) - ); - let set_point = create_signal(SpecifiedValue::from_empty_spec()); - self.insert_regulator(HalfCurvatureRegulator { - subject: subject, - measurement: measurement, - set_point: set_point - }); - } - // --- realization --- pub fn realize(&self) { From 7f21e7e999c965c793491afe5e1a67cfe8cbc4ea Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Thu, 17 Apr 2025 14:16:54 -0700 Subject: [PATCH 4/4] Centralize the curvature component index constant --- app-proto/src/assembly.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index 0eb3e64..f14f0c4 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -58,6 +58,8 @@ pub struct Element { } impl Element { + const CURVATURE_COMPONENT: usize = 3; + pub fn new( id: String, label: String, @@ -217,9 +219,10 @@ pub struct HalfCurvatureRegulator { impl HalfCurvatureRegulator { pub fn new(subject: ElementKey, assembly: &Assembly) -> HalfCurvatureRegulator { - const CURVATURE_COMPONENT: usize = 3; let measurement = assembly.elements.map( - move |elts| elts[subject].representation.with(|rep| rep[CURVATURE_COMPONENT]) + move |elts| elts[subject].representation.with( + |rep| rep[Element::CURVATURE_COMPONENT] + ) ); let set_point = create_signal(SpecifiedValue::from_empty_spec()); @@ -262,8 +265,7 @@ impl ProblemPoser for HalfCurvatureRegulator { self.set_point.with_untracked(|set_pt| { if let Some(val) = set_pt.value { if let Some(col) = elts[self.subject].column_index { - const CURVATURE_COMPONENT: usize = 3; - problem.frozen.push(CURVATURE_COMPONENT, col, val); + problem.frozen.push(Element::CURVATURE_COMPONENT, col, val); } else { panic!("Tried to write problem data from a regulator with an unindexed subject"); }