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

View file

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

View file

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

View file

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

View file

@ -228,11 +228,15 @@ pub fn Outline() -> View {
let state = use_context::<AppState>();
// 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(
|elts| elts
.clone()
.into_iter()
.sorted_by_key(|(_, elt)| elt.id().clone())
.sorted_by_key(|elt| elt.id().clone())
.collect()
);
@ -246,10 +250,10 @@ pub fn Outline() -> View {
) {
Keyed(
list=element_list,
view=|(_, elt)| view! {
view=|elt| view! {
ElementOutlineItem(element=elt)
},
key=|(_, elt)| elt.serial()
key=|elt| elt.serial()
)
}
}