From 677d7707381821e5eaf6c91de363ddbcf3a25522 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes <aaron.fenyes@fareycircles.ooo> Date: Tue, 25 Mar 2025 02:15:03 -0700 Subject: [PATCH] Let the elements and regulators write the problem When we realize an assembly, each element and regulator now writes its own data into the constraint problem. --- app-proto/src/assembly.rs | 122 +++++++++++++++++++++++++++++--------- app-proto/src/outline.rs | 4 +- 2 files changed, 95 insertions(+), 31 deletions(-) diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index 289b271..293bf73 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -121,15 +121,43 @@ impl Element { None } } + + fn write_to_problem(&self, problem: &mut ConstraintProblem) { + if let Some(index) = self.column_index { + problem.gram.push_sym(index, index, 1.0); + problem.guess.set_column(index, &self.representation.get_clone_untracked()); + } else { + panic!("Tried to write problem data from an unindexed element: \"{}\"", self.id); + } + } } #[derive(Clone, Copy)] -pub struct Regulator { +pub struct ProductRegulator { pub subjects: (ElementKey, ElementKey), pub measurement: ReadSignal<f64>, pub set_point: Signal<SpecifiedValue> } +impl ProductRegulator { + fn write_to_problem(&self, problem: &mut ConstraintProblem, elts: &Slab<Element>) { + self.set_point.with_untracked(|set_pt| { + if let Some(val) = set_pt.value { + let subjects = self.subjects; + let subject_column_indices = ( + elts[subjects.0].column_index, + elts[subjects.1].column_index + ); + if let (Some(row), Some(col)) = subject_column_indices { + problem.gram.push_sym(row, col, val); + } else { + panic!("Tried to write problem data from a regulator with an unindexed subject"); + } + } + }); + } +} + // the velocity is expressed in uniform coordinates pub struct ElementMotion<'a> { pub key: ElementKey, @@ -143,7 +171,7 @@ type AssemblyMotion<'a> = Vec<ElementMotion<'a>>; pub struct Assembly { // elements and regulators pub elements: Signal<Slab<Element>>, - pub regulators: Signal<Slab<Regulator>>, + pub regulators: Signal<Slab<ProductRegulator>>, // solution variety tangent space. the basis vectors are stored in // configuration matrix format, ordered according to the elements' column @@ -214,7 +242,7 @@ impl Assembly { ); } - fn insert_regulator(&self, regulator: Regulator) { + 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( @@ -236,7 +264,7 @@ impl Assembly { } ); let set_point = create_signal(SpecifiedValue::from_empty_spec()); - self.insert_regulator(Regulator { + self.insert_regulator(ProductRegulator { subjects: subjects, measurement: measurement, set_point: set_point @@ -263,7 +291,7 @@ impl Assembly { // edited while acting as a constraint create_effect(move || { console::log_1(&JsValue::from( - format!("Updated constraint with subjects ({}, {})", subjects.0, subjects.1) + format!("Updated regulator with subjects {:?}", subjects) )); if set_point.with(|set_pt| set_pt.is_present()) { self.realize(); @@ -274,11 +302,6 @@ impl Assembly { // --- realization --- pub fn realize(&self) { - // create a blank constraint problem - let mut problem = ConstraintProblem::new( - self.elements.with_untracked(|elts| elts.len()) - ); - // index the elements self.elements.update_silent(|elts| { for (index, (_, elt)) in elts.into_iter().enumerate() { @@ -286,29 +309,18 @@ impl Assembly { } }); - // set up the Gram matrix and the initial configuration matrix - self.elements.with_untracked(|elts| { - // set up the off-diagonal part of the Gram matrix + // set up the constraint problem + let problem = self.elements.with_untracked(|elts| { + let mut problem_to_be = ConstraintProblem::new(elts.len()); + for (_, elt) in elts { + elt.write_to_problem(&mut problem_to_be); + } self.regulators.with_untracked(|regs| { for (_, reg) in regs { - reg.set_point.with_untracked(|set_pt| { - if let Some(val) = set_pt.value { - let subjects = reg.subjects; - let row = elts[subjects.0].column_index.unwrap(); - let col = elts[subjects.1].column_index.unwrap(); - problem.gram.push_sym(row, col, val); - } - }); + reg.write_to_problem(&mut problem_to_be, elts); } }); - - // set up the initial configuration matrix and the diagonal of the - // Gram matrix - for (_, elt) in elts { - let index = elt.column_index.unwrap(); - problem.gram.push_sym(index, index, 1.0); - problem.guess.set_column(index, &elt.representation.get_clone_untracked()); - } + problem_to_be }); /* DEBUG */ @@ -464,4 +476,56 @@ impl Assembly { // sync self.realize(); } +} + +#[cfg(test)] +mod tests { + use crate::engine; + + use super::*; + + #[test] + #[should_panic(expected = "Tried to write problem data from an unindexed element: \"sphere\"")] + fn unindexed_element_test() { + let _ = create_root(|| { + Element::new( + "sphere".to_string(), + "Sphere".to_string(), + [1.0_f32, 1.0_f32, 1.0_f32], + engine::sphere(0.0, 0.0, 0.0, 1.0) + ).write_to_problem(&mut ConstraintProblem::new(1)); + }); + } + + #[test] + #[should_panic(expected = "Tried to write problem data from a regulator with an unindexed subject")] + fn unindexed_subject_test() { + let _ = create_root(|| { + let mut elts = Slab::new(); + let subjects = ( + elts.insert( + Element::new( + "sphere0".to_string(), + "Sphere 0".to_string(), + [1.0_f32, 1.0_f32, 1.0_f32], + engine::sphere(0.0, 0.0, 0.0, 1.0) + ) + ), + elts.insert( + Element::new( + "sphere1".to_string(), + "Sphere 1".to_string(), + [1.0_f32, 1.0_f32, 1.0_f32], + engine::sphere(0.0, 0.0, 0.0, 1.0) + ) + ) + ); + elts[subjects.0].column_index = Some(0); + ProductRegulator { + subjects: subjects, + measurement: create_memo(|| 0.0), + set_point: create_signal(SpecifiedValue::try_from("0.0".to_string()).unwrap()) + }.write_to_problem(&mut ConstraintProblem::new(2), &elts); + }); + } } \ No newline at end of file diff --git a/app-proto/src/outline.rs b/app-proto/src/outline.rs index 002baea..deede23 100644 --- a/app-proto/src/outline.rs +++ b/app-proto/src/outline.rs @@ -9,13 +9,13 @@ use web_sys::{ use crate::{ AppState, assembly, - assembly::{ElementKey, Regulator, RegulatorKey}, + assembly::{ElementKey, ProductRegulator, RegulatorKey}, specified::SpecifiedValue }; // an editable view of a regulator #[component(inline_props)] -fn RegulatorInput(regulator: Regulator) -> View { +fn RegulatorInput(regulator: ProductRegulator) -> View { let valid = create_signal(true); let value = create_signal( regulator.set_point.with_untracked(|set_pt| set_pt.spec.clone())