Curvature regulators #80

Merged
glen merged 21 commits from Vectornaut/dyna3:curvature-regulators into main 2025-04-21 23:40:43 +00:00
2 changed files with 95 additions and 31 deletions
Showing only changes of commit 7c40d60103 - Show all commits

View file

@ -121,15 +121,43 @@ impl Element {
None 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)] #[derive(Clone, Copy)]
pub struct Regulator { pub struct ProductRegulator {
pub subjects: (ElementKey, ElementKey), pub subjects: (ElementKey, ElementKey),
pub measurement: ReadSignal<f64>, pub measurement: ReadSignal<f64>,
pub set_point: Signal<SpecifiedValue> 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 // the velocity is expressed in uniform coordinates
pub struct ElementMotion<'a> { pub struct ElementMotion<'a> {
pub key: ElementKey, pub key: ElementKey,
@ -143,7 +171,7 @@ type AssemblyMotion<'a> = Vec<ElementMotion<'a>>;
pub struct Assembly { pub struct Assembly {
// elements and regulators // elements and regulators
pub elements: Signal<Slab<Element>>, 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 // solution variety tangent space. the basis vectors are stored in
// configuration matrix format, ordered according to the elements' column // 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 subjects = regulator.subjects;
let key = self.regulators.update(|regs| regs.insert(regulator)); let key = self.regulators.update(|regs| regs.insert(regulator));
let subject_regulators = self.elements.with( let subject_regulators = self.elements.with(
@ -236,7 +264,7 @@ impl Assembly {
} }
); );
let set_point = create_signal(SpecifiedValue::from_empty_spec()); let set_point = create_signal(SpecifiedValue::from_empty_spec());
self.insert_regulator(Regulator { self.insert_regulator(ProductRegulator {
subjects: subjects, subjects: subjects,
measurement: measurement, measurement: measurement,
set_point: set_point set_point: set_point
@ -263,7 +291,7 @@ impl Assembly {
// edited while acting as a constraint // edited while acting as a constraint
create_effect(move || { create_effect(move || {
console::log_1(&JsValue::from( 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()) { if set_point.with(|set_pt| set_pt.is_present()) {
self.realize(); self.realize();
@ -274,11 +302,6 @@ impl Assembly {
// --- realization --- // --- realization ---
pub fn realize(&self) { pub fn realize(&self) {
// create a blank constraint problem
let mut problem = ConstraintProblem::new(
self.elements.with_untracked(|elts| elts.len())
);
// index the elements // index the elements
self.elements.update_silent(|elts| { self.elements.update_silent(|elts| {
for (index, (_, elt)) in elts.into_iter().enumerate() { for (index, (_, elt)) in elts.into_iter().enumerate() {
@ -286,29 +309,18 @@ impl Assembly {
} }
}); });
// set up the Gram matrix and the initial configuration matrix // set up the constraint problem
self.elements.with_untracked(|elts| { let problem = self.elements.with_untracked(|elts| {
// set up the off-diagonal part of the Gram matrix 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| { self.regulators.with_untracked(|regs| {
for (_, reg) in regs { for (_, reg) in regs {
reg.set_point.with_untracked(|set_pt| { reg.write_to_problem(&mut problem_to_be, elts);
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);
}
});
} }
}); });
problem_to_be
// 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());
}
}); });
/* DEBUG */ /* DEBUG */
@ -465,3 +477,55 @@ impl Assembly {
self.realize(); 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);
});
}
}

View file

@ -9,13 +9,13 @@ use web_sys::{
use crate::{ use crate::{
AppState, AppState,
assembly, assembly,
assembly::{ElementKey, Regulator, RegulatorKey}, assembly::{ElementKey, ProductRegulator, RegulatorKey},
specified::SpecifiedValue specified::SpecifiedValue
}; };
// an editable view of a regulator // an editable view of a regulator
#[component(inline_props)] #[component(inline_props)]
fn RegulatorInput(regulator: Regulator) -> View { fn RegulatorInput(regulator: ProductRegulator) -> View {
let valid = create_signal(true); let valid = create_signal(true);
let value = create_signal( let value = create_signal(
regulator.set_point.with_untracked(|set_pt| set_pt.spec.clone()) regulator.set_point.with_untracked(|set_pt| set_pt.spec.clone())