forked from StudioInfinity/dyna3
Generalize the element insertion methods
This commit is contained in:
parent
f9df459a0d
commit
873de78f2d
2 changed files with 91 additions and 70 deletions
|
@ -1,3 +1,4 @@
|
||||||
|
use std::rc::Rc;
|
||||||
use sycamore::prelude::*;
|
use sycamore::prelude::*;
|
||||||
use web_sys::{console, wasm_bindgen::JsValue};
|
use web_sys::{console, wasm_bindgen::JsValue};
|
||||||
|
|
||||||
|
@ -11,7 +12,7 @@ use crate::{
|
||||||
// load an example assembly for testing. this code will be removed once we've
|
// load an example assembly for testing. this code will be removed once we've
|
||||||
// built a more formal test assembly system
|
// built a more formal test assembly system
|
||||||
fn load_gen_assemb(assembly: &Assembly) {
|
fn load_gen_assemb(assembly: &Assembly) {
|
||||||
let _ = assembly.try_insert_sphere(
|
let _ = assembly.try_insert_element(
|
||||||
Sphere::new(
|
Sphere::new(
|
||||||
String::from("gemini_a"),
|
String::from("gemini_a"),
|
||||||
String::from("Castor"),
|
String::from("Castor"),
|
||||||
|
@ -19,7 +20,7 @@ fn load_gen_assemb(assembly: &Assembly) {
|
||||||
engine::sphere(0.5, 0.5, 0.0, 1.0)
|
engine::sphere(0.5, 0.5, 0.0, 1.0)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
let _ = assembly.try_insert_sphere(
|
let _ = assembly.try_insert_element(
|
||||||
Sphere::new(
|
Sphere::new(
|
||||||
String::from("gemini_b"),
|
String::from("gemini_b"),
|
||||||
String::from("Pollux"),
|
String::from("Pollux"),
|
||||||
|
@ -27,7 +28,7 @@ fn load_gen_assemb(assembly: &Assembly) {
|
||||||
engine::sphere(-0.5, -0.5, 0.0, 1.0)
|
engine::sphere(-0.5, -0.5, 0.0, 1.0)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
let _ = assembly.try_insert_sphere(
|
let _ = assembly.try_insert_element(
|
||||||
Sphere::new(
|
Sphere::new(
|
||||||
String::from("ursa_major"),
|
String::from("ursa_major"),
|
||||||
String::from("Ursa major"),
|
String::from("Ursa major"),
|
||||||
|
@ -35,7 +36,7 @@ fn load_gen_assemb(assembly: &Assembly) {
|
||||||
engine::sphere(-0.5, 0.5, 0.0, 0.75)
|
engine::sphere(-0.5, 0.5, 0.0, 0.75)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
let _ = assembly.try_insert_sphere(
|
let _ = assembly.try_insert_element(
|
||||||
Sphere::new(
|
Sphere::new(
|
||||||
String::from("ursa_minor"),
|
String::from("ursa_minor"),
|
||||||
String::from("Ursa minor"),
|
String::from("Ursa minor"),
|
||||||
|
@ -43,7 +44,7 @@ fn load_gen_assemb(assembly: &Assembly) {
|
||||||
engine::sphere(0.5, -0.5, 0.0, 0.5)
|
engine::sphere(0.5, -0.5, 0.0, 0.5)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
let _ = assembly.try_insert_sphere(
|
let _ = assembly.try_insert_element(
|
||||||
Sphere::new(
|
Sphere::new(
|
||||||
String::from("moon_deimos"),
|
String::from("moon_deimos"),
|
||||||
String::from("Deimos"),
|
String::from("Deimos"),
|
||||||
|
@ -51,7 +52,7 @@ fn load_gen_assemb(assembly: &Assembly) {
|
||||||
engine::sphere(0.0, 0.15, 1.0, 0.25)
|
engine::sphere(0.0, 0.15, 1.0, 0.25)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
let _ = assembly.try_insert_sphere(
|
let _ = assembly.try_insert_element(
|
||||||
Sphere::new(
|
Sphere::new(
|
||||||
String::from("moon_phobos"),
|
String::from("moon_phobos"),
|
||||||
String::from("Phobos"),
|
String::from("Phobos"),
|
||||||
|
@ -66,7 +67,7 @@ fn load_gen_assemb(assembly: &Assembly) {
|
||||||
// built a more formal test assembly system
|
// built a more formal test assembly system
|
||||||
fn load_low_curv_assemb(assembly: &Assembly) {
|
fn load_low_curv_assemb(assembly: &Assembly) {
|
||||||
let a = 0.75_f64.sqrt();
|
let a = 0.75_f64.sqrt();
|
||||||
let _ = assembly.try_insert_sphere(
|
let _ = assembly.try_insert_element(
|
||||||
Sphere::new(
|
Sphere::new(
|
||||||
"central".to_string(),
|
"central".to_string(),
|
||||||
"Central".to_string(),
|
"Central".to_string(),
|
||||||
|
@ -74,7 +75,7 @@ fn load_low_curv_assemb(assembly: &Assembly) {
|
||||||
engine::sphere(0.0, 0.0, 0.0, 1.0)
|
engine::sphere(0.0, 0.0, 0.0, 1.0)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
let _ = assembly.try_insert_sphere(
|
let _ = assembly.try_insert_element(
|
||||||
Sphere::new(
|
Sphere::new(
|
||||||
"assemb_plane".to_string(),
|
"assemb_plane".to_string(),
|
||||||
"Assembly plane".to_string(),
|
"Assembly plane".to_string(),
|
||||||
|
@ -82,7 +83,7 @@ fn load_low_curv_assemb(assembly: &Assembly) {
|
||||||
engine::sphere_with_offset(0.0, 0.0, 1.0, 0.0, 0.0)
|
engine::sphere_with_offset(0.0, 0.0, 1.0, 0.0, 0.0)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
let _ = assembly.try_insert_sphere(
|
let _ = assembly.try_insert_element(
|
||||||
Sphere::new(
|
Sphere::new(
|
||||||
"side1".to_string(),
|
"side1".to_string(),
|
||||||
"Side 1".to_string(),
|
"Side 1".to_string(),
|
||||||
|
@ -90,7 +91,7 @@ fn load_low_curv_assemb(assembly: &Assembly) {
|
||||||
engine::sphere_with_offset(1.0, 0.0, 0.0, 1.0, 0.0)
|
engine::sphere_with_offset(1.0, 0.0, 0.0, 1.0, 0.0)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
let _ = assembly.try_insert_sphere(
|
let _ = assembly.try_insert_element(
|
||||||
Sphere::new(
|
Sphere::new(
|
||||||
"side2".to_string(),
|
"side2".to_string(),
|
||||||
"Side 2".to_string(),
|
"Side 2".to_string(),
|
||||||
|
@ -98,7 +99,7 @@ fn load_low_curv_assemb(assembly: &Assembly) {
|
||||||
engine::sphere_with_offset(-0.5, a, 0.0, 1.0, 0.0)
|
engine::sphere_with_offset(-0.5, a, 0.0, 1.0, 0.0)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
let _ = assembly.try_insert_sphere(
|
let _ = assembly.try_insert_element(
|
||||||
Sphere::new(
|
Sphere::new(
|
||||||
"side3".to_string(),
|
"side3".to_string(),
|
||||||
"Side 3".to_string(),
|
"Side 3".to_string(),
|
||||||
|
@ -106,7 +107,7 @@ fn load_low_curv_assemb(assembly: &Assembly) {
|
||||||
engine::sphere_with_offset(-0.5, -a, 0.0, 1.0, 0.0)
|
engine::sphere_with_offset(-0.5, -a, 0.0, 1.0, 0.0)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
let _ = assembly.try_insert_sphere(
|
let _ = assembly.try_insert_element(
|
||||||
Sphere::new(
|
Sphere::new(
|
||||||
"corner1".to_string(),
|
"corner1".to_string(),
|
||||||
"Corner 1".to_string(),
|
"Corner 1".to_string(),
|
||||||
|
@ -114,7 +115,7 @@ fn load_low_curv_assemb(assembly: &Assembly) {
|
||||||
engine::sphere(-4.0/3.0, 0.0, 0.0, 1.0/3.0)
|
engine::sphere(-4.0/3.0, 0.0, 0.0, 1.0/3.0)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
let _ = assembly.try_insert_sphere(
|
let _ = assembly.try_insert_element(
|
||||||
Sphere::new(
|
Sphere::new(
|
||||||
"corner2".to_string(),
|
"corner2".to_string(),
|
||||||
"Corner 2".to_string(),
|
"Corner 2".to_string(),
|
||||||
|
@ -122,7 +123,7 @@ fn load_low_curv_assemb(assembly: &Assembly) {
|
||||||
engine::sphere(2.0/3.0, -4.0/3.0 * a, 0.0, 1.0/3.0)
|
engine::sphere(2.0/3.0, -4.0/3.0 * a, 0.0, 1.0/3.0)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
let _ = assembly.try_insert_sphere(
|
let _ = assembly.try_insert_element(
|
||||||
Sphere::new(
|
Sphere::new(
|
||||||
String::from("corner3"),
|
String::from("corner3"),
|
||||||
String::from("Corner 3"),
|
String::from("Corner 3"),
|
||||||
|
@ -167,7 +168,7 @@ pub fn AddRemove() -> View {
|
||||||
button(
|
button(
|
||||||
on:click=|_| {
|
on:click=|_| {
|
||||||
let state = use_context::<AppState>();
|
let state = use_context::<AppState>();
|
||||||
state.assembly.insert_new_sphere();
|
state.assembly.insert_element_default::<Sphere>();
|
||||||
}
|
}
|
||||||
) { "+" }
|
) { "+" }
|
||||||
button(
|
button(
|
||||||
|
@ -190,7 +191,7 @@ pub fn AddRemove() -> View {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
);
|
);
|
||||||
state.assembly.insert_regulator(
|
state.assembly.insert_regulator(
|
||||||
InversiveDistanceRegulator::new(subjects, &state.assembly)
|
Rc::new(InversiveDistanceRegulator::new(subjects, &state.assembly))
|
||||||
);
|
);
|
||||||
state.selection.update(|sel| sel.clear());
|
state.selection.update(|sel| sel.clear());
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,6 +43,23 @@ pub trait ProblemPoser {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Element: ProblemPoser + DisplayItem {
|
pub trait Element: ProblemPoser + DisplayItem {
|
||||||
|
// the default identifier for an element of this type
|
||||||
|
fn default_id() -> String where Self: Sized;
|
||||||
|
|
||||||
|
// create the default example of an element of this type
|
||||||
|
fn default(id: String, id_num: u64) -> Self where Self: Sized;
|
||||||
|
|
||||||
|
// the regulators that should be created when an element of this type is
|
||||||
|
// inserted into the given assembly with the given storage key
|
||||||
|
/* KLUDGE */
|
||||||
|
// right now, this organization makes sense because regulators identify
|
||||||
|
// their subjects by storage key, so the element has to be inserted before
|
||||||
|
// its regulators can be created. if we change the way regulators identify
|
||||||
|
// their subjects, we should consider refactoring
|
||||||
|
fn default_regulators(_key: ElementKey, _assembly: &Assembly) -> Vec<Rc<dyn Regulator>> where Self: Sized {
|
||||||
|
Vec::new()
|
||||||
|
}
|
||||||
|
|
||||||
fn id(&self) -> &String;
|
fn id(&self) -> &String;
|
||||||
fn label(&self) -> &String;
|
fn label(&self) -> &String;
|
||||||
fn representation(&self) -> Signal<DVector<f64>>;
|
fn representation(&self) -> Signal<DVector<f64>>;
|
||||||
|
@ -54,6 +71,19 @@ pub trait Element: ProblemPoser + DisplayItem {
|
||||||
// a serial number that uniquely identifies this element
|
// a serial number that uniquely identifies this element
|
||||||
fn serial(&self) -> u64;
|
fn serial(&self) -> u64;
|
||||||
|
|
||||||
|
// take the next serial number, panicking if that was the last one left
|
||||||
|
fn next_serial() -> u64 where Self: Sized {
|
||||||
|
// the technique we use to panic on overflow is taken from _Rust Atomics
|
||||||
|
// and Locks_, by Mara Bos
|
||||||
|
//
|
||||||
|
// https://marabos.nl/atomics/atomics.html#example-handle-overflow
|
||||||
|
//
|
||||||
|
NEXT_ELEMENT_SERIAL.fetch_update(
|
||||||
|
Ordering::SeqCst, Ordering::SeqCst,
|
||||||
|
|serial| serial.checked_add(1)
|
||||||
|
).expect("Out of serial numbers for elements")
|
||||||
|
}
|
||||||
|
|
||||||
// the configuration matrix column index that was assigned to the element
|
// the configuration matrix column index that was assigned to the element
|
||||||
// last time the assembly was realized, or `None` if the element has never
|
// last time the assembly was realized, or `None` if the element has never
|
||||||
// been through a realization
|
// been through a realization
|
||||||
|
@ -98,30 +128,36 @@ impl Sphere {
|
||||||
color: ElementColor,
|
color: ElementColor,
|
||||||
representation: DVector<f64>
|
representation: DVector<f64>
|
||||||
) -> Sphere {
|
) -> Sphere {
|
||||||
// take the next serial number, panicking if that was the last number we
|
|
||||||
// had left. the technique we use to panic on overflow is taken from
|
|
||||||
// _Rust Atomics and Locks_, by Mara Bos
|
|
||||||
//
|
|
||||||
// https://marabos.nl/atomics/atomics.html#example-handle-overflow
|
|
||||||
//
|
|
||||||
let serial = NEXT_ELEMENT_SERIAL.fetch_update(
|
|
||||||
Ordering::SeqCst, Ordering::SeqCst,
|
|
||||||
|serial| serial.checked_add(1)
|
|
||||||
).expect("Out of serial numbers for elements");
|
|
||||||
|
|
||||||
Sphere {
|
Sphere {
|
||||||
id: id,
|
id: id,
|
||||||
label: label,
|
label: label,
|
||||||
color: color,
|
color: color,
|
||||||
representation: create_signal(representation),
|
representation: create_signal(representation),
|
||||||
regulators: create_signal(BTreeSet::default()),
|
regulators: create_signal(BTreeSet::default()),
|
||||||
serial: serial,
|
serial: Self::next_serial(),
|
||||||
column_index: None.into()
|
column_index: None.into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Element for Sphere {
|
impl Element for Sphere {
|
||||||
|
fn default_id() -> String {
|
||||||
|
"sphere".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default(id: String, id_num: u64) -> Sphere {
|
||||||
|
Sphere::new(
|
||||||
|
id,
|
||||||
|
format!("Sphere {id_num}"),
|
||||||
|
[0.75_f32, 0.75_f32, 0.75_f32],
|
||||||
|
sphere(0.0, 0.0, 0.0, 1.0)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_regulators(key: ElementKey, assembly: &Assembly) -> Vec<Rc<dyn Regulator>> {
|
||||||
|
vec![Rc::new(HalfCurvatureRegulator::new(key, assembly))]
|
||||||
|
}
|
||||||
|
|
||||||
fn id(&self) -> &String {
|
fn id(&self) -> &String {
|
||||||
&self.id
|
&self.id
|
||||||
}
|
}
|
||||||
|
@ -336,63 +372,58 @@ impl Assembly {
|
||||||
|
|
||||||
// --- inserting elements and regulators ---
|
// --- inserting elements and regulators ---
|
||||||
|
|
||||||
// insert a sphere into the assembly without checking whether we already
|
// insert an element into the assembly without checking whether we already
|
||||||
// have an element with the same identifier. any element that does have the
|
// have an element with the same identifier. any element that does have the
|
||||||
// same identifier will get kicked out of the `elements_by_id` index
|
// same identifier will get kicked out of the `elements_by_id` index
|
||||||
fn insert_sphere_unchecked(&self, sphere: Sphere) -> ElementKey {
|
fn insert_element_unchecked<T: Element + 'static>(&self, elt: T) -> ElementKey {
|
||||||
// insert the sphere
|
// insert the element
|
||||||
let id = sphere.id.clone();
|
let id = elt.id().clone();
|
||||||
let key = self.elements.update(|elts| elts.insert(Rc::new(sphere)));
|
let key = self.elements.update(|elts| elts.insert(Rc::new(elt)));
|
||||||
self.elements_by_id.update(|elts_by_id| elts_by_id.insert(id, key));
|
self.elements_by_id.update(|elts_by_id| elts_by_id.insert(id, key));
|
||||||
|
|
||||||
// regulate the sphere's curvature
|
// create and insert the element's default regulators
|
||||||
self.insert_regulator(HalfCurvatureRegulator::new(key, &self));
|
for reg in T::default_regulators(key, &self) {
|
||||||
|
self.insert_regulator(reg);
|
||||||
|
}
|
||||||
|
|
||||||
key
|
key
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn try_insert_sphere(&self, sphere: Sphere) -> Option<ElementKey> {
|
pub fn try_insert_element(&self, elt: impl Element + 'static) -> Option<ElementKey> {
|
||||||
let can_insert = self.elements_by_id.with_untracked(
|
let can_insert = self.elements_by_id.with_untracked(
|
||||||
|elts_by_id| !elts_by_id.contains_key(&sphere.id)
|
|elts_by_id| !elts_by_id.contains_key(elt.id())
|
||||||
);
|
);
|
||||||
if can_insert {
|
if can_insert {
|
||||||
Some(self.insert_sphere_unchecked(sphere))
|
Some(self.insert_element_unchecked(elt))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_new_sphere(&self) {
|
pub fn insert_element_default<T: Element + 'static>(&self) {
|
||||||
// find the next unused identifier in the default sequence
|
// find the next unused identifier in the default sequence
|
||||||
|
let default_id = T::default_id();
|
||||||
let mut id_num = 1;
|
let mut id_num = 1;
|
||||||
let mut id = format!("sphere{}", id_num);
|
let mut id = format!("{default_id}{id_num}");
|
||||||
while self.elements_by_id.with_untracked(
|
while self.elements_by_id.with_untracked(
|
||||||
|elts_by_id| elts_by_id.contains_key(&id)
|
|elts_by_id| elts_by_id.contains_key(&id)
|
||||||
) {
|
) {
|
||||||
id_num += 1;
|
id_num += 1;
|
||||||
id = format!("sphere{}", id_num);
|
id = format!("{default_id}{id_num}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// create and insert a sphere
|
// create and insert the default example of `T`
|
||||||
let _ = self.insert_sphere_unchecked(
|
let _ = self.insert_element_unchecked(T::default(id, id_num));
|
||||||
Sphere::new(
|
|
||||||
id,
|
|
||||||
format!("Sphere {}", id_num),
|
|
||||||
[0.75_f32, 0.75_f32, 0.75_f32],
|
|
||||||
sphere(0.0, 0.0, 0.0, 1.0)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn insert_regulator<T: Regulator + 'static>(&self, regulator: T) {
|
pub fn insert_regulator(&self, regulator: Rc<dyn Regulator>) {
|
||||||
// add the regulator to the assembly's regulator list
|
// add the regulator to the assembly's regulator list
|
||||||
let regulator_rc = Rc::new(regulator);
|
|
||||||
let key = self.regulators.update(
|
let key = self.regulators.update(
|
||||||
|regs| regs.insert(regulator_rc.clone())
|
|regs| regs.insert(regulator.clone())
|
||||||
);
|
);
|
||||||
|
|
||||||
// add the regulator to each subject's regulator list
|
// add the regulator to each subject's regulator list
|
||||||
let subjects = regulator_rc.subjects();
|
let subjects = regulator.subjects();
|
||||||
let subject_regulators: Vec<_> = self.elements.with_untracked(
|
let subject_regulators: Vec<_> = self.elements.with_untracked(
|
||||||
|elts| subjects.into_iter().map(
|
|elts| subjects.into_iter().map(
|
||||||
|subj| elts[subj].regulators()
|
|subj| elts[subj].regulators()
|
||||||
|
@ -409,10 +440,10 @@ impl Assembly {
|
||||||
/* DEBUG */
|
/* DEBUG */
|
||||||
// log the regulator update
|
// log the regulator update
|
||||||
console::log_1(&JsValue::from(
|
console::log_1(&JsValue::from(
|
||||||
format!("Updated regulator with subjects {:?}", regulator_rc.subjects())
|
format!("Updated regulator with subjects {:?}", regulator.subjects())
|
||||||
));
|
));
|
||||||
|
|
||||||
if regulator_rc.try_activate(&self_for_effect) {
|
if regulator.try_activate(&self_for_effect) {
|
||||||
self_for_effect.realize();
|
self_for_effect.realize();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -621,20 +652,14 @@ impl Assembly {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::engine;
|
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "Sphere \"sphere\" should be indexed before writing problem data")]
|
#[should_panic(expected = "Sphere \"sphere\" should be indexed before writing problem data")]
|
||||||
fn unindexed_element_test() {
|
fn unindexed_element_test() {
|
||||||
let _ = create_root(|| {
|
let _ = create_root(|| {
|
||||||
Sphere::new(
|
let elt = Sphere::default("sphere".to_string(), 0);
|
||||||
"sphere".to_string(),
|
elt.pose(&mut ConstraintProblem::new(1), &Slab::new());
|
||||||
"Sphere".to_string(),
|
|
||||||
[1.0_f32, 1.0_f32, 1.0_f32],
|
|
||||||
engine::sphere(0.0, 0.0, 0.0, 1.0)
|
|
||||||
).pose(&mut ConstraintProblem::new(1), &Slab::new());
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -645,12 +670,7 @@ mod tests {
|
||||||
let mut elts = Slab::<Rc<dyn Element>>::new();
|
let mut elts = Slab::<Rc<dyn Element>>::new();
|
||||||
let subjects = [0, 1].map(|k| {
|
let subjects = [0, 1].map(|k| {
|
||||||
elts.insert(
|
elts.insert(
|
||||||
Rc::new(Sphere::new(
|
Rc::new(Sphere::default(format!("sphere{k}"), k))
|
||||||
format!("sphere{k}"),
|
|
||||||
format!("Sphere {k}"),
|
|
||||||
[1.0_f32, 1.0_f32, 1.0_f32],
|
|
||||||
engine::sphere(0.0, 0.0, 0.0, 1.0)
|
|
||||||
))
|
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
elts[subjects[0]].set_column_index(0);
|
elts[subjects[0]].set_column_index(0);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue