forked from StudioInfinity/dyna3
chore: uniformize error messages, 80-char lines, import fmt::Display
This commit is contained in:
parent
c081f1a809
commit
c0e6ebf3d6
2 changed files with 64 additions and 34 deletions
|
@ -31,9 +31,14 @@ The latest prototype is in the folder `app-proto`. It includes both a user inter
|
||||||
3. Call `rustup target add wasm32-unknown-unknown` to add the [most generic 32-bit WebAssembly target](https://doc.rust-lang.org/nightly/rustc/platform-support/wasm32-unknown-unknown.html)
|
3. Call `rustup target add wasm32-unknown-unknown` to add the [most generic 32-bit WebAssembly target](https://doc.rust-lang.org/nightly/rustc/platform-support/wasm32-unknown-unknown.html)
|
||||||
4. Call `cargo install wasm-pack` to install the [WebAssembly toolchain](https://rustwasm.github.io/docs/wasm-pack/)
|
4. Call `cargo install wasm-pack` to install the [WebAssembly toolchain](https://rustwasm.github.io/docs/wasm-pack/)
|
||||||
5. Call `cargo install trunk` to install the [Trunk](https://trunkrs.dev/) web-build tool
|
5. Call `cargo install trunk` to install the [Trunk](https://trunkrs.dev/) web-build tool
|
||||||
|
- In the future, `trunk` can be updated with the same command. You may
|
||||||
|
need the `--locked` flag if your ambient version of `rustc` does not
|
||||||
|
match that required by `trunk`.
|
||||||
6. Add the `.cargo/bin` folder in your home directory to your executable search path
|
6. Add the `.cargo/bin` folder in your home directory to your executable search path
|
||||||
- This lets you call Trunk, and other tools installed by Cargo, without specifying their paths
|
- This lets you call Trunk, and other tools installed by Cargo, without specifying their paths
|
||||||
- On POSIX systems, the search path is stored in the `PATH` environment variable
|
- On POSIX systems, the search path is stored in the `PATH` environment variable
|
||||||
|
- Alternatively, if you don't want to adjust your `PATH`, you can install
|
||||||
|
`trunk` in another directory `DIR` via `cargo install --root DIR trunk`
|
||||||
|
|
||||||
### Play with the prototype
|
### Play with the prototype
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
collections::{BTreeMap, BTreeSet},
|
collections::{BTreeMap, BTreeSet},
|
||||||
fmt,
|
fmt,
|
||||||
fmt::{Debug, Formatter},
|
fmt::{Debug, Display, Formatter},
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::{atomic, atomic::AtomicU64},
|
sync::{atomic, atomic::AtomicU64},
|
||||||
|
@ -128,7 +128,7 @@ pub trait Element: Serial + ProblemPoser + DisplayItem {
|
||||||
|
|
||||||
impl Debug for dyn Element {
|
impl Debug for dyn Element {
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
self.id().fmt(f)
|
Debug::fmt(&self.id(), f)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -248,13 +248,19 @@ impl Serial for Sphere {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn index_message(thing: &str, name: &str, actor: &str) -> String {
|
||||||
|
format!(
|
||||||
|
"{thing} \"{name}\" must be indexed before {actor} writes problem data"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
impl ProblemPoser for Sphere {
|
impl ProblemPoser for Sphere {
|
||||||
fn pose(&self, problem: &mut ConstraintProblem) {
|
fn pose(&self, problem: &mut ConstraintProblem) {
|
||||||
let index = self.column_index().expect(
|
let index = self.column_index().expect(
|
||||||
format!("Sphere \"{}\" should be indexed before writing problem data", self.id).as_str()
|
index_message("Sphere", &self.id, "it").as_str());
|
||||||
);
|
|
||||||
problem.gram.push_sym(index, index, 1.0);
|
problem.gram.push_sym(index, index, 1.0);
|
||||||
problem.guess.set_column(index, &self.representation.get_clone_untracked());
|
problem.guess.set_column(
|
||||||
|
index, &self.representation.get_clone_untracked());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -357,11 +363,11 @@ impl Serial for Point {
|
||||||
impl ProblemPoser for Point {
|
impl ProblemPoser for Point {
|
||||||
fn pose(&self, problem: &mut ConstraintProblem) {
|
fn pose(&self, problem: &mut ConstraintProblem) {
|
||||||
let index = self.column_index().expect(
|
let index = self.column_index().expect(
|
||||||
format!("Point \"{}\" should be indexed before writing problem data", self.id).as_str()
|
index_message("Point", &self.id, "it").as_str());
|
||||||
);
|
|
||||||
problem.gram.push_sym(index, index, 0.0);
|
problem.gram.push_sym(index, index, 0.0);
|
||||||
problem.frozen.push(Self::WEIGHT_COMPONENT, index, 0.5);
|
problem.frozen.push(Self::WEIGHT_COMPONENT, index, 0.5);
|
||||||
problem.guess.set_column(index, &self.representation.get_clone_untracked());
|
problem.guess.set_column(
|
||||||
|
index, &self.representation.get_clone_untracked());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -406,7 +412,8 @@ pub struct InversiveDistanceRegulator {
|
||||||
|
|
||||||
impl InversiveDistanceRegulator {
|
impl InversiveDistanceRegulator {
|
||||||
pub fn new(subjects: [Rc<dyn Element>; 2]) -> Self {
|
pub fn new(subjects: [Rc<dyn Element>; 2]) -> Self {
|
||||||
let representations = subjects.each_ref().map(|subj| subj.representation());
|
let representations = subjects.each_ref().map(
|
||||||
|
|subj| subj.representation());
|
||||||
let measurement = create_memo(move || {
|
let measurement = create_memo(move || {
|
||||||
representations[0].with(|rep_0|
|
representations[0].with(|rep_0|
|
||||||
representations[1].with(|rep_1|
|
representations[1].with(|rep_1|
|
||||||
|
@ -448,8 +455,8 @@ impl ProblemPoser for InversiveDistanceRegulator {
|
||||||
if let Some(val) = set_pt.value {
|
if let Some(val) = set_pt.value {
|
||||||
let [row, col] = self.subjects.each_ref().map(
|
let [row, col] = self.subjects.each_ref().map(
|
||||||
|subj| subj.column_index().expect(
|
|subj| subj.column_index().expect(
|
||||||
"Subjects should be indexed before inversive distance regulator writes problem data"
|
index_message("Subject", subj.id(),
|
||||||
)
|
"inversive distance regulator").as_str())
|
||||||
);
|
);
|
||||||
problem.gram.push_sym(row, col, val);
|
problem.gram.push_sym(row, col, val);
|
||||||
}
|
}
|
||||||
|
@ -502,8 +509,8 @@ impl ProblemPoser for HalfCurvatureRegulator {
|
||||||
self.set_point.with_untracked(|set_pt| {
|
self.set_point.with_untracked(|set_pt| {
|
||||||
if let Some(val) = set_pt.value {
|
if let Some(val) = set_pt.value {
|
||||||
let col = self.subject.column_index().expect(
|
let col = self.subject.column_index().expect(
|
||||||
"Subject should be indexed before half-curvature regulator writes problem data"
|
index_message("Subject", &self.subject.id,
|
||||||
);
|
"half-curvature regulator").as_str());
|
||||||
problem.frozen.push(Sphere::CURVATURE_COMPONENT, col, val);
|
problem.frozen.push(Sphere::CURVATURE_COMPONENT, col, val);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -519,7 +526,7 @@ impl Axis {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Axis {
|
impl Display for Axis {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.write_str(self.name())
|
f.write_str(self.name())
|
||||||
}
|
}
|
||||||
|
@ -539,7 +546,9 @@ impl PointCoordinateRegulator {
|
||||||
move |rep| rep[axis as usize]
|
move |rep| rep[axis as usize]
|
||||||
);
|
);
|
||||||
let set_point = create_signal(SpecifiedValue::from_empty_spec());
|
let set_point = create_signal(SpecifiedValue::from_empty_spec());
|
||||||
Self { subject, axis, measurement, set_point, serial: Self::next_serial() }
|
Self {
|
||||||
|
subject, axis, measurement, set_point, serial: Self::next_serial()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -558,10 +567,11 @@ impl ProblemPoser for PointCoordinateRegulator {
|
||||||
self.set_point.with_untracked(|set_pt| {
|
self.set_point.with_untracked(|set_pt| {
|
||||||
if let Some(val) = set_pt.value {
|
if let Some(val) = set_pt.value {
|
||||||
let col = self.subject.column_index().expect(
|
let col = self.subject.column_index().expect(
|
||||||
"Subject must be indexed before point-coordinate regulator poses.");
|
index_message("Subject", &self.subject.id,
|
||||||
|
"point-coordinate regulator").as_str());
|
||||||
problem.frozen.push(self.axis as usize, col, val);
|
problem.frozen.push(self.axis as usize, col, val);
|
||||||
// Check if all three spatial coordinates have been frozen, and if so,
|
// If all three of the subject's spatial coordinates have been
|
||||||
// freeze the norm component as well
|
// frozen, then freeze its norm component:
|
||||||
let mut coords = [0.0; Axis::CARDINALITY];
|
let mut coords = [0.0; Axis::CARDINALITY];
|
||||||
let mut nset: usize = 0;
|
let mut nset: usize = 0;
|
||||||
for &MatrixEntry {index, value} in &(problem.frozen) {
|
for &MatrixEntry {index, value} in &(problem.frozen) {
|
||||||
|
@ -573,7 +583,9 @@ impl ProblemPoser for PointCoordinateRegulator {
|
||||||
if nset == Axis::CARDINALITY {
|
if nset == Axis::CARDINALITY {
|
||||||
let [x, y, z] = coords;
|
let [x, y, z] = coords;
|
||||||
problem.frozen.push(
|
problem.frozen.push(
|
||||||
Point::NORM_COMPONENT, col, point(x,y,z)[Point::NORM_COMPONENT]);
|
Point::NORM_COMPONENT,
|
||||||
|
col,
|
||||||
|
point(x,y,z)[Point::NORM_COMPONENT]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -672,7 +684,8 @@ impl Assembly {
|
||||||
let id = elt.id().clone();
|
let id = elt.id().clone();
|
||||||
let elt_rc = Rc::new(elt);
|
let elt_rc = Rc::new(elt);
|
||||||
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() {
|
||||||
|
@ -748,7 +761,8 @@ impl Assembly {
|
||||||
pub fn load_config(&self, config: &DMatrix<f64>) {
|
pub fn load_config(&self, config: &DMatrix<f64>) {
|
||||||
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()))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -893,7 +907,8 @@ impl Assembly {
|
||||||
if column_index < realized_dim {
|
if column_index < realized_dim {
|
||||||
// this element had a column index when we started, so by
|
// this element had a column index when we started, so by
|
||||||
// invariant (1), it's reflected in the tangent space
|
// invariant (1), it's reflected in the tangent space
|
||||||
let mut target_columns = motion_proj.columns_mut(0, realized_dim);
|
let mut target_columns =
|
||||||
|
motion_proj.columns_mut(0, realized_dim);
|
||||||
target_columns += self.tangent.with(
|
target_columns += self.tangent.with(
|
||||||
|tan| tan.proj(&elt_motion.velocity, column_index)
|
|tan| tan.proj(&elt_motion.velocity, column_index)
|
||||||
);
|
);
|
||||||
|
@ -901,9 +916,10 @@ impl Assembly {
|
||||||
// this element didn't have a column index when we started, so
|
// this element didn't have a column index when we started, so
|
||||||
// by invariant (2), it's unconstrained
|
// by invariant (2), it's unconstrained
|
||||||
let mut target_column = motion_proj.column_mut(column_index);
|
let mut target_column = motion_proj.column_mut(column_index);
|
||||||
let unif_to_std = elt_motion.element.representation().with_untracked(
|
let unif_to_std = elt_motion
|
||||||
|rep| local_unif_to_std(rep.as_view())
|
.element.representation()
|
||||||
);
|
.with_untracked(
|
||||||
|
|rep| local_unif_to_std(rep.as_view()));
|
||||||
target_column += unif_to_std * elt_motion.velocity;
|
target_column += unif_to_std * elt_motion.velocity;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -920,7 +936,9 @@ impl Assembly {
|
||||||
elt.project_to_normalized(rep);
|
elt.project_to_normalized(rep);
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
console_log!("No velocity to unpack for fresh element \"{}\"", elt.id())
|
console_log!(
|
||||||
|
"No velocity to unpack for fresh element \"{}\"",
|
||||||
|
elt.id())
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -940,7 +958,8 @@ mod tests {
|
||||||
use crate::engine;
|
use crate::engine;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "Sphere \"sphere\" should be indexed before writing problem data")]
|
#[should_panic(expected =
|
||||||
|
"Sphere \"sphere\" must be indexed before it writes problem data")]
|
||||||
fn unindexed_element_test() {
|
fn unindexed_element_test() {
|
||||||
let _ = create_root(|| {
|
let _ = create_root(|| {
|
||||||
let elt = Sphere::default("sphere".to_string(), 0);
|
let elt = Sphere::default("sphere".to_string(), 0);
|
||||||
|
@ -949,17 +968,20 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[should_panic(expected = "Subjects should be indexed before inversive distance regulator writes problem data")]
|
#[should_panic(expected = "Subject \"sphere1\" must be indexed before \
|
||||||
|
inversive distance regulator writes problem data")]
|
||||||
fn unindexed_subject_test_inversive_distance() {
|
fn unindexed_subject_test_inversive_distance() {
|
||||||
let _ = create_root(|| {
|
let _ = create_root(|| {
|
||||||
let subjects = [0, 1].map(
|
let subjects = [0, 1].map(
|
||||||
|k| Rc::new(Sphere::default(format!("sphere{k}"), k)) as Rc<dyn Element>
|
|k| Rc::new(Sphere::default(
|
||||||
|
format!("sphere{k}"), k)) as Rc<dyn Element>
|
||||||
);
|
);
|
||||||
subjects[0].set_column_index(0);
|
subjects[0].set_column_index(0);
|
||||||
InversiveDistanceRegulator {
|
InversiveDistanceRegulator {
|
||||||
subjects: subjects,
|
subjects: subjects,
|
||||||
measurement: create_memo(|| 0.0),
|
measurement: create_memo(|| 0.0),
|
||||||
set_point: create_signal(SpecifiedValue::try_from("0.0".to_string()).unwrap()),
|
set_point: create_signal(
|
||||||
|
SpecifiedValue::try_from("0.0".to_string()).unwrap()),
|
||||||
serial: InversiveDistanceRegulator::next_serial()
|
serial: InversiveDistanceRegulator::next_serial()
|
||||||
}.pose(&mut ConstraintProblem::new(2));
|
}.pose(&mut ConstraintProblem::new(2));
|
||||||
});
|
});
|
||||||
|
@ -988,8 +1010,10 @@ mod tests {
|
||||||
// nudge the sphere repeatedly along the `z` axis
|
// nudge the sphere repeatedly along the `z` axis
|
||||||
const STEP_SIZE: f64 = 0.0025;
|
const STEP_SIZE: f64 = 0.0025;
|
||||||
const STEP_CNT: usize = 400;
|
const STEP_CNT: usize = 400;
|
||||||
let sphere = assembly.elements_by_id.with(|elts_by_id| elts_by_id[sphere_id].clone());
|
let sphere = assembly.elements_by_id.with(
|
||||||
let velocity = DVector::from_column_slice(&[0.0, 0.0, STEP_SIZE, 0.0]);
|
|elts_by_id| elts_by_id[sphere_id].clone());
|
||||||
|
let velocity =
|
||||||
|
DVector::from_column_slice(&[0.0, 0.0, STEP_SIZE, 0.0]);
|
||||||
for _ in 0..STEP_CNT {
|
for _ in 0..STEP_CNT {
|
||||||
assembly.deform(
|
assembly.deform(
|
||||||
vec![
|
vec![
|
||||||
|
@ -1007,7 +1031,8 @@ mod tests {
|
||||||
let final_half_curv = sphere.representation().with_untracked(
|
let final_half_curv = sphere.representation().with_untracked(
|
||||||
|rep| rep[Sphere::CURVATURE_COMPONENT]
|
|rep| rep[Sphere::CURVATURE_COMPONENT]
|
||||||
);
|
);
|
||||||
assert!((final_half_curv / INITIAL_HALF_CURV - 1.0).abs() < DRIFT_TOL);
|
assert!((final_half_curv / INITIAL_HALF_CURV - 1.0).abs()
|
||||||
|
< DRIFT_TOL);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Add a link
Reference in a new issue