Compare commits

..

4 commits

Author SHA1 Message Date
Aaron Fenyes
7f21e7e999 Centralize the curvature component index constant 2025-04-17 14:16:54 -07:00
Aaron Fenyes
52d99755f9 Give each regulator a constructor
The assembly shouldn't have to know how to construct regulators.
2025-04-17 14:10:07 -07:00
Aaron Fenyes
8f8e806d12 Move pointer creation into insert_regulator
This will make it easier to give each regulator a constructor instead of
an "insert new" method.
2025-04-17 14:02:37 -07:00
Aaron Fenyes
ee8a01b9cb 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.
2025-04-17 13:20:50 -07:00
2 changed files with 82 additions and 71 deletions

View file

@ -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());
}
) { "🔗" }

View file

@ -58,6 +58,8 @@ pub struct Element {
}
impl Element {
const CURVATURE_COMPONENT: usize = 3;
pub fn new(
id: String,
label: String,
@ -145,6 +147,8 @@ pub trait Regulator: ProblemPoser + OutlineItem {
fn subjects(&self) -> Vec<ElementKey>;
fn measurement(&self) -> ReadSignal<f64>;
fn set_point(&self) -> Signal<SpecifiedValue>;
fn activate(&self, _assembly: &Assembly) {}
}
pub struct ProductRegulator {
@ -153,6 +157,29 @@ pub struct ProductRegulator {
pub set_point: Signal<SpecifiedValue>
}
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<ElementKey> {
self.subjects.into()
@ -190,6 +217,24 @@ pub struct HalfCurvatureRegulator {
pub set_point: Signal<SpecifiedValue>
}
impl HalfCurvatureRegulator {
pub fn new(subject: ElementKey, assembly: &Assembly) -> HalfCurvatureRegulator {
let measurement = assembly.elements.map(
move |elts| elts[subject].representation.with(
|rep| rep[Element::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<ElementKey> {
vec![self.subject]
@ -202,6 +247,17 @@ impl Regulator for HalfCurvatureRegulator {
fn set_point(&self) -> Signal<SpecifiedValue> {
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 {
@ -209,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");
}
@ -272,7 +327,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
}
@ -310,11 +365,15 @@ impl Assembly {
);
}
fn insert_regulator(&self, regulator: Rc<dyn Regulator>) {
let subjects = regulator.subjects();
pub fn insert_regulator<T: Regulator + 'static>(&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)
|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
@ -324,6 +383,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_rc.subjects())
));
if regulator_rc.set_point().with(|set_pt| set_pt.is_present()) {
regulator_rc.activate(&self_for_effect);
self_for_effect.realize();
}
});
/* DEBUG */
// print an updated list of regulators
console::log_1(&JsValue::from("Regulators:"));
@ -347,69 +419,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(Rc::new(ProductRegulator {
subjects: subjects,
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) {
// 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(Rc::new(HalfCurvatureRegulator {
subject: subject,
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 ---
pub fn realize(&self) {