Use pointers to refer to elements and regulators #84

Merged
glen merged 10 commits from Vectornaut/dyna3:use-pointers into main 2025-05-06 19:17:31 +00:00
5 changed files with 74 additions and 50 deletions
Showing only changes of commit c6b628d424 - Show all commits

12
app-proto/Cargo.lock generated
View file

@ -1,6 +1,6 @@
# This file is automatically @generated by Cargo. # This file is automatically @generated by Cargo.
# It is not intended for manual editing. # It is not intended for manual editing.
version = 3 version = 4
[[package]] [[package]]
name = "ahash" name = "ahash"
@ -90,7 +90,6 @@ dependencies = [
"nalgebra", "nalgebra",
"readonly", "readonly",
"rustc-hash", "rustc-hash",
"slab",
"sycamore", "sycamore",
"wasm-bindgen-test", "wasm-bindgen-test",
"web-sys", "web-sys",
@ -414,15 +413,6 @@ dependencies = [
"wide", "wide",
] ]
[[package]]
name = "slab"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67"
dependencies = [
"autocfg",
]
[[package]] [[package]]
name = "slotmap" name = "slotmap"
version = "1.0.7" version = "1.0.7"

View file

@ -16,7 +16,6 @@ lazy_static = "1.5.0"
nalgebra = "0.33.0" nalgebra = "0.33.0"
readonly = "0.2.12" readonly = "0.2.12"
rustc-hash = "2.0.0" rustc-hash = "2.0.0"
slab = "0.4.9"
sycamore = "0.9.0-beta.3" sycamore = "0.9.0-beta.3"
# The `console_error_panic_hook` crate provides better debugging of panics by # The `console_error_panic_hook` crate provides better debugging of panics by

View file

@ -1,14 +1,15 @@
use nalgebra::{DMatrix, DVector, DVectorView}; use nalgebra::{DMatrix, DVector, DVectorView};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use slab::Slab;
use std::{ use std::{
any::{Any, TypeId}, any::{Any, TypeId},
cell::Cell, cell::Cell,
collections::BTreeSet,
cmp::Ordering,
fmt, fmt,
fmt::{Debug, Formatter}, fmt::{Debug, Formatter},
hash::{Hash, Hasher}, hash::{Hash, Hasher},
rc::Rc, rc::Rc,
sync::atomic::{AtomicU64, Ordering} sync::{atomic, atomic::AtomicU64}
}; };
use sycamore::prelude::*; use sycamore::prelude::*;
use web_sys::{console, wasm_bindgen::JsValue}; /* DEBUG */ use web_sys::{console, wasm_bindgen::JsValue}; /* DEBUG */
@ -29,9 +30,6 @@ use crate::{
specified::SpecifiedValue specified::SpecifiedValue
}; };
// the types of the keys we use to access an assembly's elements
pub type ElementKey = usize;
pub type ElementColor = [f32; 3]; pub type ElementColor = [f32; 3];
/* KLUDGE */ /* KLUDGE */
@ -53,7 +51,7 @@ pub trait Serial {
// https://marabos.nl/atomics/atomics.html#example-handle-overflow // https://marabos.nl/atomics/atomics.html#example-handle-overflow
// //
NEXT_SERIAL.fetch_update( NEXT_SERIAL.fetch_update(
Ordering::SeqCst, Ordering::SeqCst, atomic::Ordering::SeqCst, atomic::Ordering::SeqCst,
|serial| serial.checked_add(1) |serial| serial.checked_add(1)
).expect("Out of serial numbers for elements") ).expect("Out of serial numbers for elements")
} }
@ -73,6 +71,18 @@ impl PartialEq for dyn Serial {
impl Eq for dyn Serial {} impl Eq for dyn Serial {}
impl PartialOrd for dyn Serial {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for dyn Serial {
fn cmp(&self, other: &Self) -> Ordering {
self.serial().cmp(&other.serial())
}
}
pub trait ProblemPoser { pub trait ProblemPoser {
fn pose(&self, problem: &mut ConstraintProblem); fn pose(&self, problem: &mut ConstraintProblem);
} }
@ -95,7 +105,7 @@ pub trait Element: Serial + ProblemPoser + DisplayItem {
// the regulators the element is subject to. the assembly that owns the // the regulators the element is subject to. the assembly that owns the
// element is responsible for keeping this set up to date // element is responsible for keeping this set up to date
fn regulators(&self) -> Signal<Vec<Rc<dyn Regulator>>>; fn regulators(&self) -> Signal<BTreeSet<Rc<dyn Regulator>>>;
// 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
@ -128,12 +138,24 @@ impl PartialEq for dyn Element {
impl Eq for dyn Element {} impl Eq for dyn Element {}
impl PartialOrd for dyn Element {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
<dyn Serial>::partial_cmp(self, other)
}
}
impl Ord for dyn Element {
fn cmp(&self, other: &Self) -> Ordering {
<dyn Serial>::cmp(self, other)
}
}
pub struct Sphere { pub struct Sphere {
pub id: String, pub id: String,
pub label: String, pub label: String,
pub color: ElementColor, pub color: ElementColor,
pub representation: Signal<DVector<f64>>, pub representation: Signal<DVector<f64>>,
pub regulators: Signal<Vec<Rc<dyn Regulator>>>, pub regulators: Signal<BTreeSet<Rc<dyn Regulator>>>,
serial: u64, serial: u64,
column_index: Cell<Option<usize>> column_index: Cell<Option<usize>>
} }
@ -152,7 +174,7 @@ impl Sphere {
label: label, label: label,
color: color, color: color,
representation: create_signal(representation), representation: create_signal(representation),
regulators: create_signal(Vec::new()), regulators: create_signal(BTreeSet::new()),
serial: Self::next_serial(), serial: Self::next_serial(),
column_index: None.into() column_index: None.into()
} }
@ -189,7 +211,7 @@ impl Element for Sphere {
self.representation self.representation
} }
fn regulators(&self) -> Signal<Vec<Rc<dyn Regulator>>> { fn regulators(&self) -> Signal<BTreeSet<Rc<dyn Regulator>>> {
self.regulators self.regulators
} }
@ -223,7 +245,7 @@ pub struct Point {
pub label: String, pub label: String,
pub color: ElementColor, pub color: ElementColor,
pub representation: Signal<DVector<f64>>, pub representation: Signal<DVector<f64>>,
pub regulators: Signal<Vec<Rc<dyn Regulator>>>, pub regulators: Signal<BTreeSet<Rc<dyn Regulator>>>,
serial: u64, serial: u64,
column_index: Cell<Option<usize>> column_index: Cell<Option<usize>>
} }
@ -242,7 +264,7 @@ impl Point {
label, label,
color, color,
representation: create_signal(representation), representation: create_signal(representation),
regulators: create_signal(Vec::new()), regulators: create_signal(BTreeSet::new()),
serial: Self::next_serial(), serial: Self::next_serial(),
column_index: None.into() column_index: None.into()
} }
@ -275,7 +297,7 @@ impl Element for Point {
self.representation self.representation
} }
fn regulators(&self) -> Signal<Vec<Rc<dyn Regulator>>> { fn regulators(&self) -> Signal<BTreeSet<Rc<dyn Regulator>>> {
self.regulators self.regulators
} }
@ -335,6 +357,18 @@ impl PartialEq for dyn Regulator {
impl Eq for dyn Regulator {} impl Eq for dyn Regulator {}
impl PartialOrd for dyn Regulator {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
<dyn Serial>::partial_cmp(self, other)
}
}
impl Ord for dyn Regulator {
fn cmp(&self, other: &Self) -> Ordering {
<dyn Serial>::cmp(self, other)
}
}
pub struct InversiveDistanceRegulator { pub struct InversiveDistanceRegulator {
pub subjects: [Rc<dyn Element>; 2], pub subjects: [Rc<dyn Element>; 2],
pub measurement: ReadSignal<f64>, pub measurement: ReadSignal<f64>,
@ -472,8 +506,8 @@ type AssemblyMotion<'a> = Vec<ElementMotion<'a>>;
#[derive(Clone)] #[derive(Clone)]
pub struct Assembly { pub struct Assembly {
// elements and regulators // elements and regulators
pub elements: Signal<Slab<Rc<dyn Element>>>, pub elements: Signal<BTreeSet<Rc<dyn Element>>>,
pub regulators: Signal<Slab<Rc<dyn Regulator>>>, pub regulators: Signal<BTreeSet<Rc<dyn Regulator>>>,
// 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
@ -494,8 +528,8 @@ pub struct Assembly {
impl Assembly { impl Assembly {
pub fn new() -> Assembly { pub fn new() -> Assembly {
Assembly { Assembly {
elements: create_signal(Slab::new()), elements: create_signal(BTreeSet::new()),
regulators: create_signal(Slab::new()), regulators: create_signal(BTreeSet::new()),
tangent: create_signal(ConfigSubspace::zero(0)), tangent: create_signal(ConfigSubspace::zero(0)),
elements_by_id: create_signal(FxHashMap::default()) elements_by_id: create_signal(FxHashMap::default())
} }
@ -506,30 +540,27 @@ impl Assembly {
// insert an element 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_element_unchecked(&self, elt: impl Element + 'static) -> ElementKey { fn insert_element_unchecked(&self, elt: impl Element + 'static) {
// insert the element // insert the element
let id = elt.id().clone(); let id = elt.id().clone();
let elt_rc = Rc::new(elt); let elt_rc = Rc::new(elt);
let key = self.elements.update(|elts| elts.insert(elt_rc.clone())); self.elements.update(|elts| elts.insert(elt_rc.clone()));
self.elements_by_id.update(|elts_by_id| elts_by_id.insert(id, elt_rc.clone())); self.elements_by_id.update(|elts_by_id| elts_by_id.insert(id, elt_rc.clone()));
// create and insert the element's default regulators // create and insert the element's default regulators
for reg in elt_rc.default_regulators() { for reg in elt_rc.default_regulators() {
self.insert_regulator(reg); self.insert_regulator(reg);
} }
key
} }
pub fn try_insert_element(&self, elt: impl Element + 'static) -> Option<ElementKey> { pub fn try_insert_element(&self, elt: impl Element + 'static) -> bool {
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(elt.id()) |elts_by_id| !elts_by_id.contains_key(elt.id())
); );
if can_insert { if can_insert {
Some(self.insert_element_unchecked(elt)) self.insert_element_unchecked(elt);
} else {
None
} }
can_insert
} }
pub fn insert_element_default<T: Element + 'static>(&self) { pub fn insert_element_default<T: Element + 'static>(&self) {
@ -559,7 +590,7 @@ impl Assembly {
|subj| subj.regulators() |subj| subj.regulators()
).collect(); ).collect();
for regulators in subject_regulators { for regulators in subject_regulators {
regulators.update(|regs| regs.push(regulator.clone())); regulators.update(|regs| regs.insert(regulator.clone()));
} }
// update the realization when the regulator becomes a constraint, or is // update the realization when the regulator becomes a constraint, or is
@ -581,7 +612,7 @@ impl Assembly {
// print an updated list of regulators // print an updated list of regulators
console::log_1(&JsValue::from("Regulators:")); console::log_1(&JsValue::from("Regulators:"));
self.regulators.with_untracked(|regs| { self.regulators.with_untracked(|regs| {
for (_, reg) in regs.into_iter() { for reg in regs.into_iter() {
console::log_1(&JsValue::from(format!( console::log_1(&JsValue::from(format!(
" {:?}: {}", " {:?}: {}",
reg.subjects(), reg.subjects(),
@ -605,7 +636,7 @@ impl Assembly {
pub fn realize(&self) { pub fn realize(&self) {
// 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.iter().enumerate() {
elt.set_column_index(index); elt.set_column_index(index);
} }
}); });
@ -613,11 +644,11 @@ impl Assembly {
// set up the constraint problem // set up the constraint problem
let problem = self.elements.with_untracked(|elts| { let problem = self.elements.with_untracked(|elts| {
let mut problem = ConstraintProblem::new(elts.len()); let mut problem = ConstraintProblem::new(elts.len());
for (_, elt) in elts { for elt in elts {
elt.pose(&mut problem); elt.pose(&mut problem);
} }
self.regulators.with_untracked(|regs| { self.regulators.with_untracked(|regs| {
for (_, reg) in regs { for reg in regs {
reg.pose(&mut problem); reg.pose(&mut problem);
} }
}); });
@ -660,7 +691,7 @@ impl Assembly {
if success { if success {
// read out the solution // read out the solution
for (_, elt) in self.elements.get_clone_untracked() { for elt in self.elements.get_clone_untracked() {
elt.representation().update( elt.representation().update(
|rep| rep.set_column(0, &config.column(elt.column_index().unwrap())) |rep| rep.set_column(0, &config.column(elt.column_index().unwrap()))
); );
@ -741,7 +772,7 @@ impl Assembly {
// normalizations, so we restore those afterward // normalizations, so we restore those afterward
/* KLUDGE */ /* KLUDGE */
// for now, we only restore the normalizations of spheres // for now, we only restore the normalizations of spheres
for (_, elt) in self.elements.get_clone_untracked() { for elt in self.elements.get_clone_untracked() {
elt.representation().update_silent(|rep| { elt.representation().update_silent(|rep| {
match elt.column_index() { match elt.column_index() {
Some(column_index) => { Some(column_index) => {

View file

@ -363,7 +363,7 @@ pub fn Display() -> View {
let scene_changed = create_signal(true); let scene_changed = create_signal(true);
create_effect(move || { create_effect(move || {
state.assembly.elements.with(|elts| { state.assembly.elements.with(|elts| {
for (_, elt) in elts { for elt in elts {
elt.representation().track(); elt.representation().track();
} }
}); });
@ -616,7 +616,7 @@ pub fn Display() -> View {
// set up the scene // set up the scene
state.assembly.elements.with_untracked( state.assembly.elements.with_untracked(
|elts| for (_, elt) in elts { |elts| for elt in elts {
let selected = state.selection.with(|sel| sel.contains(elt)); let selected = state.selection.with(|sel| sel.contains(elt));
elt.show(&mut scene, selected); elt.show(&mut scene, selected);
} }
@ -851,7 +851,7 @@ pub fn Display() -> View {
let (dir, pixel_size) = event_dir(&event); let (dir, pixel_size) = event_dir(&event);
console::log_1(&JsValue::from(dir.to_string())); console::log_1(&JsValue::from(dir.to_string()));
let mut clicked: Option<(Rc<dyn Element>, f64)> = None; let mut clicked: Option<(Rc<dyn Element>, f64)> = None;
for (_, elt) in state.assembly.elements.get_clone_untracked() { for elt in state.assembly.elements.get_clone_untracked() {
match assembly_to_world.with(|asm_to_world| elt.cast(dir, asm_to_world, pixel_size)) { match assembly_to_world.with(|asm_to_world| elt.cast(dir, asm_to_world, pixel_size)) {
Some(depth) => match clicked { Some(depth) => match clicked {
Some((_, best_depth)) => { Some((_, best_depth)) => {

View file

@ -228,11 +228,15 @@ pub fn Outline() -> View {
let state = use_context::<AppState>(); let state = use_context::<AppState>();
// list the elements alphabetically by ID // list the elements alphabetically by ID
/* TO DO */
// this code is designed to generalize easily to other sort keys. if we only
// ever wanted to sort by ID, we could do that more simply using the
// `elements_by_id` index
let element_list = state.assembly.elements.map( let element_list = state.assembly.elements.map(
|elts| elts |elts| elts
.clone() .clone()
.into_iter() .into_iter()
.sorted_by_key(|(_, elt)| elt.id().clone()) .sorted_by_key(|elt| elt.id().clone())
.collect() .collect()
); );
@ -246,10 +250,10 @@ pub fn Outline() -> View {
) { ) {
Keyed( Keyed(
list=element_list, list=element_list,
view=|(_, elt)| view! { view=|elt| view! {
ElementOutlineItem(element=elt) ElementOutlineItem(element=elt)
}, },
key=|(_, elt)| elt.serial() key=|elt| elt.serial()
) )
} }
} }