93 lines
2.8 KiB
Rust
93 lines
2.8 KiB
Rust
|
use nalgebra::DVector;
|
||
|
use rustc_hash::FxHashMap;
|
||
|
use slab::Slab;
|
||
|
use std::collections::BTreeSet;
|
||
|
use sycamore::prelude::*;
|
||
|
|
||
|
#[derive(Clone, PartialEq)]
|
||
|
pub struct Element {
|
||
|
pub id: String,
|
||
|
pub label: String,
|
||
|
pub color: [f32; 3],
|
||
|
pub rep: DVector<f64>,
|
||
|
pub constraints: BTreeSet<usize>
|
||
|
}
|
||
|
|
||
|
#[derive(Clone)]
|
||
|
pub struct Constraint {
|
||
|
pub args: (usize, usize),
|
||
|
pub rep: f64,
|
||
|
pub active: Signal<bool>
|
||
|
}
|
||
|
|
||
|
// a complete, view-independent description of an assembly
|
||
|
#[derive(Clone)]
|
||
|
pub struct Assembly {
|
||
|
// elements and constraints
|
||
|
pub elements: Signal<Slab<Element>>,
|
||
|
pub constraints: Signal<Slab<Constraint>>,
|
||
|
|
||
|
// indexing
|
||
|
pub elements_by_id: Signal<FxHashMap<String, usize>>
|
||
|
}
|
||
|
|
||
|
impl Assembly {
|
||
|
pub fn new() -> Assembly {
|
||
|
Assembly {
|
||
|
elements: create_signal(Slab::new()),
|
||
|
constraints: create_signal(Slab::new()),
|
||
|
elements_by_id: create_signal(FxHashMap::default())
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// 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: Element) {
|
||
|
let id = elt.id.clone();
|
||
|
let key = self.elements.update(|elts| elts.insert(elt));
|
||
|
self.elements_by_id.update(|elts_by_id| elts_by_id.insert(id, key));
|
||
|
}
|
||
|
|
||
|
pub fn try_insert_element(&self, elt: Element) -> bool {
|
||
|
let can_insert = self.elements_by_id.with_untracked(
|
||
|
|elts_by_id| !elts_by_id.contains_key(&elt.id)
|
||
|
);
|
||
|
if can_insert {
|
||
|
self.insert_element_unchecked(elt);
|
||
|
}
|
||
|
can_insert
|
||
|
}
|
||
|
|
||
|
pub fn insert_new_element(&self) {
|
||
|
// find the next unused identifier in the default sequence
|
||
|
let mut id_num = 1;
|
||
|
let mut id = format!("sphere{}", id_num);
|
||
|
while self.elements_by_id.with_untracked(
|
||
|
|elts_by_id| elts_by_id.contains_key(&id)
|
||
|
) {
|
||
|
id_num += 1;
|
||
|
id = format!("sphere{}", id_num);
|
||
|
}
|
||
|
|
||
|
// create and insert a new element
|
||
|
self.insert_element_unchecked(
|
||
|
Element {
|
||
|
id: id,
|
||
|
label: format!("Sphere {}", id_num),
|
||
|
color: [0.75_f32, 0.75_f32, 0.75_f32],
|
||
|
rep: DVector::<f64>::from_column_slice(&[0.0, 0.0, 0.0, 0.5, -0.5]),
|
||
|
constraints: BTreeSet::default()
|
||
|
}
|
||
|
);
|
||
|
}
|
||
|
|
||
|
pub fn insert_constraint(&self, constraint: Constraint) {
|
||
|
let args = constraint.args;
|
||
|
let key = self.constraints.update(|csts| csts.insert(constraint));
|
||
|
self.elements.update(|elts| {
|
||
|
elts[args.0].constraints.insert(key);
|
||
|
elts[args.1].constraints.insert(key);
|
||
|
})
|
||
|
}
|
||
|
}
|