forked from StudioInfinity/dyna3
Compare commits
No commits in common. "f1da5155a3115f073c8f2f54e7811924d41a7275" and "2adf4669f47ab8f2bff8d64ac011a2bd09f632fa" have entirely different histories.
f1da5155a3
...
2adf4669f4
3 changed files with 52 additions and 107 deletions
|
@ -46,9 +46,6 @@ features = [
|
||||||
dyna3 = { path = ".", default-features = false, features = ["dev"] }
|
dyna3 = { path = ".", default-features = false, features = ["dev"] }
|
||||||
wasm-bindgen-test = "0.3.34"
|
wasm-bindgen-test = "0.3.34"
|
||||||
|
|
||||||
[lints.rust]
|
|
||||||
unexpected_cfgs = { level = "warn", check-cfg = ["cfg(sycamore_force_ssr)"] }
|
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
opt-level = "s" # optimize for small code size
|
opt-level = "s" # optimize for small code size
|
||||||
debug = true # include debug symbols
|
debug = true # include debug symbols
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
use nalgebra::{DMatrix, DVector, DVectorView};
|
use nalgebra::{DMatrix, DVector, DVectorView};
|
||||||
use std::{
|
use std::{
|
||||||
|
any::{Any, TypeId},
|
||||||
cell::Cell,
|
cell::Cell,
|
||||||
collections::{BTreeMap, BTreeSet},
|
collections::{BTreeMap, BTreeSet},
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
|
@ -18,8 +19,6 @@ use crate::{
|
||||||
Q,
|
Q,
|
||||||
change_half_curvature,
|
change_half_curvature,
|
||||||
local_unif_to_std,
|
local_unif_to_std,
|
||||||
normalize_mut_point,
|
|
||||||
normalize_mut_sphere,
|
|
||||||
point,
|
point,
|
||||||
realize_gram,
|
realize_gram,
|
||||||
sphere,
|
sphere,
|
||||||
|
@ -107,9 +106,6 @@ pub trait Element: Serial + ProblemPoser + DisplayItem {
|
||||||
// element is responsible for keeping this set up to date
|
// element is responsible for keeping this set up to date
|
||||||
fn regulators(&self) -> Signal<BTreeSet<Rc<dyn Regulator>>>;
|
fn regulators(&self) -> Signal<BTreeSet<Rc<dyn Regulator>>>;
|
||||||
|
|
||||||
// normalize a representation vector for this kind of element
|
|
||||||
fn normalize_mut_rep(&self, rep: &mut DVector<f64>);
|
|
||||||
|
|
||||||
// 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
|
||||||
|
@ -218,10 +214,6 @@ impl Element for Sphere {
|
||||||
self.regulators
|
self.regulators
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normalize_mut_rep(&self, rep: &mut DVector<f64>) {
|
|
||||||
normalize_mut_sphere(rep);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn column_index(&self) -> Option<usize> {
|
fn column_index(&self) -> Option<usize> {
|
||||||
self.column_index.get()
|
self.column_index.get()
|
||||||
}
|
}
|
||||||
|
@ -308,10 +300,6 @@ impl Element for Point {
|
||||||
self.regulators
|
self.regulators
|
||||||
}
|
}
|
||||||
|
|
||||||
fn normalize_mut_rep(&self, rep: &mut DVector<f64>) {
|
|
||||||
normalize_mut_point(rep);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn column_index(&self) -> Option<usize> {
|
fn column_index(&self) -> Option<usize> {
|
||||||
self.column_index.get()
|
self.column_index.get()
|
||||||
}
|
}
|
||||||
|
@ -610,7 +598,9 @@ impl Assembly {
|
||||||
create_effect(move || {
|
create_effect(move || {
|
||||||
/* DEBUG */
|
/* DEBUG */
|
||||||
// log the regulator update
|
// log the regulator update
|
||||||
console_log!("Updated regulator with subjects {:?}", regulator.subjects());
|
console::log_1(&JsValue::from(
|
||||||
|
format!("Updated regulator with subjects {:?}", regulator.subjects())
|
||||||
|
));
|
||||||
|
|
||||||
if regulator.try_activate() {
|
if regulator.try_activate() {
|
||||||
self_for_effect.realize();
|
self_for_effect.realize();
|
||||||
|
@ -619,10 +609,10 @@ impl Assembly {
|
||||||
|
|
||||||
/* DEBUG */
|
/* DEBUG */
|
||||||
// print an updated list of regulators
|
// print an updated list of regulators
|
||||||
console_log!("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!(
|
console::log_1(&JsValue::from(format!(
|
||||||
" {:?}: {}",
|
" {:?}: {}",
|
||||||
reg.subjects(),
|
reg.subjects(),
|
||||||
reg.set_point().with_untracked(
|
reg.set_point().with_untracked(
|
||||||
|
@ -635,7 +625,7 @@ impl Assembly {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
)));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -666,11 +656,19 @@ impl Assembly {
|
||||||
|
|
||||||
/* DEBUG */
|
/* DEBUG */
|
||||||
// log the Gram matrix
|
// log the Gram matrix
|
||||||
console_log!("Gram matrix:\n{}", problem.gram);
|
console::log_1(&JsValue::from("Gram matrix:"));
|
||||||
|
problem.gram.log_to_console();
|
||||||
|
|
||||||
/* DEBUG */
|
/* DEBUG */
|
||||||
// log the initial configuration matrix
|
// log the initial configuration matrix
|
||||||
console_log!("Old configuration:{:>8.3}", problem.guess);
|
console::log_1(&JsValue::from("Old configuration:"));
|
||||||
|
for j in 0..problem.guess.nrows() {
|
||||||
|
let mut row_str = String::new();
|
||||||
|
for k in 0..problem.guess.ncols() {
|
||||||
|
row_str.push_str(format!(" {:>8.3}", problem.guess[(j, k)]).as_str());
|
||||||
|
}
|
||||||
|
console::log_1(&JsValue::from(row_str));
|
||||||
|
}
|
||||||
|
|
||||||
// look for a configuration with the given Gram matrix
|
// look for a configuration with the given Gram matrix
|
||||||
let (config, tangent, success, history) = realize_gram(
|
let (config, tangent, success, history) = realize_gram(
|
||||||
|
@ -679,14 +677,16 @@ impl Assembly {
|
||||||
|
|
||||||
/* DEBUG */
|
/* DEBUG */
|
||||||
// report the outcome of the search
|
// report the outcome of the search
|
||||||
|
console::log_1(&JsValue::from(
|
||||||
if success {
|
if success {
|
||||||
console_log!("Target accuracy achieved!")
|
"Target accuracy achieved!"
|
||||||
} else {
|
} else {
|
||||||
console_log!("Failed to reach target accuracy")
|
"Failed to reach target accuracy"
|
||||||
}
|
}
|
||||||
console_log!("Steps: {}", history.scaled_loss.len() - 1);
|
));
|
||||||
console_log!("Loss: {}", *history.scaled_loss.last().unwrap());
|
console::log_2(&JsValue::from("Steps:"), &JsValue::from(history.scaled_loss.len() - 1));
|
||||||
console_log!("Tangent dimension: {}", tangent.dim());
|
console::log_2(&JsValue::from("Loss:"), &JsValue::from(*history.scaled_loss.last().unwrap()));
|
||||||
|
console::log_2(&JsValue::from("Tangent dimension:"), &JsValue::from(tangent.dim()));
|
||||||
|
|
||||||
if success {
|
if success {
|
||||||
// read out the solution
|
// read out the solution
|
||||||
|
@ -769,17 +769,29 @@ impl Assembly {
|
||||||
|
|
||||||
// step the assembly along the deformation. this changes the elements'
|
// step the assembly along the deformation. this changes the elements'
|
||||||
// normalizations, so we restore those afterward
|
// 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| {
|
elt.representation().update_silent(|rep| {
|
||||||
match elt.column_index() {
|
match elt.column_index() {
|
||||||
Some(column_index) => {
|
Some(column_index) => {
|
||||||
// step the element along the deformation and then
|
// step the assembly along the deformation
|
||||||
// restore its normalization
|
|
||||||
*rep += motion_proj.column(column_index);
|
*rep += motion_proj.column(column_index);
|
||||||
elt.normalize_mut_rep(rep);
|
|
||||||
|
if elt.type_id() == TypeId::of::<Sphere>() {
|
||||||
|
// restore normalization by contracting toward the
|
||||||
|
// last coordinate axis
|
||||||
|
let q_sp = rep.fixed_rows::<3>(0).norm_squared();
|
||||||
|
let half_q_lt = -2.0 * rep[3] * rep[4];
|
||||||
|
let half_q_lt_sq = half_q_lt * half_q_lt;
|
||||||
|
let scaling = half_q_lt + (q_sp + half_q_lt_sq).sqrt();
|
||||||
|
rep.fixed_rows_mut::<4>(0).scale_mut(1.0 / scaling);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
None => {
|
None => {
|
||||||
console_log!("No velocity to unpack for fresh element \"{}\"", elt.id())
|
console::log_1(&JsValue::from(
|
||||||
|
format!("No velocity to unpack for fresh element \"{}\"", elt.id())
|
||||||
|
))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -796,8 +808,6 @@ impl Assembly {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use crate::engine;
|
|
||||||
|
|
||||||
#[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() {
|
||||||
|
@ -823,50 +833,4 @@ mod tests {
|
||||||
}.pose(&mut ConstraintProblem::new(2));
|
}.pose(&mut ConstraintProblem::new(2));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn curvature_drift_test() {
|
|
||||||
const INITIAL_RADIUS: f64 = 0.25;
|
|
||||||
let _ = create_root(|| {
|
|
||||||
// set up an assembly containing a single sphere centered at the
|
|
||||||
// origin
|
|
||||||
let assembly = Assembly::new();
|
|
||||||
let sphere_id = "sphere0";
|
|
||||||
let _ = assembly.try_insert_element(
|
|
||||||
// we create the sphere by hand for two reasons: to choose the
|
|
||||||
// curvature (which can affect drift rate) and to make the test
|
|
||||||
// independent of `Sphere::default`
|
|
||||||
Sphere::new(
|
|
||||||
String::from(sphere_id),
|
|
||||||
String::from("Sphere 0"),
|
|
||||||
[0.75_f32, 0.75_f32, 0.75_f32],
|
|
||||||
engine::sphere(0.0, 0.0, 0.0, INITIAL_RADIUS)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
// nudge the sphere repeatedly along the `z` axis
|
|
||||||
const STEP_SIZE: f64 = 0.0025;
|
|
||||||
const STEP_CNT: usize = 400;
|
|
||||||
let sphere = assembly.elements_by_id.with(|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 {
|
|
||||||
assembly.deform(
|
|
||||||
vec![
|
|
||||||
ElementMotion {
|
|
||||||
element: sphere.clone(),
|
|
||||||
velocity: velocity.as_view()
|
|
||||||
}
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check how much the sphere's curvature has drifted
|
|
||||||
const INITIAL_HALF_CURV: f64 = 0.5 / INITIAL_RADIUS;
|
|
||||||
const DRIFT_TOL: f64 = 0.015;
|
|
||||||
let final_half_curv = sphere.representation().with_untracked(
|
|
||||||
|rep| rep[Sphere::CURVATURE_COMPONENT]
|
|
||||||
);
|
|
||||||
assert!((final_half_curv / INITIAL_HALF_CURV - 1.0).abs() < DRIFT_TOL);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -1,6 +1,5 @@
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use nalgebra::{Const, DMatrix, DVector, DVectorView, Dyn, SymmetricEigen};
|
use nalgebra::{Const, DMatrix, DVector, DVectorView, Dyn, SymmetricEigen};
|
||||||
use std::fmt::{Display, Error, Formatter};
|
|
||||||
use web_sys::{console, wasm_bindgen::JsValue}; /* DEBUG */
|
use web_sys::{console, wasm_bindgen::JsValue}; /* DEBUG */
|
||||||
|
|
||||||
// --- elements ---
|
// --- elements ---
|
||||||
|
@ -35,21 +34,6 @@ pub fn sphere_with_offset(dir_x: f64, dir_y: f64, dir_z: f64, off: f64, curv: f6
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
|
||||||
// normalize a sphere's representation vector by contracting toward the last
|
|
||||||
// coordinate axis
|
|
||||||
pub fn normalize_mut_sphere(rep: &mut DVector<f64>) {
|
|
||||||
let q_sp = rep.fixed_rows::<3>(0).norm_squared();
|
|
||||||
let half_q_lt = -2.0 * rep[3] * rep[4];
|
|
||||||
let half_q_lt_sq = half_q_lt * half_q_lt;
|
|
||||||
let scaling = half_q_lt + (q_sp + half_q_lt_sq).sqrt();
|
|
||||||
rep.fixed_rows_mut::<4>(0).scale_mut(1.0 / scaling);
|
|
||||||
}
|
|
||||||
|
|
||||||
// normalize a point's representation vector by scaling
|
|
||||||
pub fn normalize_mut_point(rep: &mut DVector<f64>) {
|
|
||||||
rep.scale_mut(0.5 / rep[3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// given a sphere's representation vector, change the sphere's half-curvature to
|
// given a sphere's representation vector, change the sphere's half-curvature to
|
||||||
// `half-curv` and then restore normalization by contracting the representation
|
// `half-curv` and then restore normalization by contracting the representation
|
||||||
// vector toward the curvature axis
|
// vector toward the curvature axis
|
||||||
|
@ -110,6 +94,15 @@ impl PartialMatrix {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* DEBUG */
|
||||||
|
pub fn log_to_console(&self) {
|
||||||
|
for &MatrixEntry { index: (row, col), value } in self {
|
||||||
|
console::log_1(&JsValue::from(
|
||||||
|
format!(" {} {} {}", row, col, value)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn freeze(&self, a: &DMatrix<f64>) -> DMatrix<f64> {
|
fn freeze(&self, a: &DMatrix<f64>) -> DMatrix<f64> {
|
||||||
let mut result = a.clone();
|
let mut result = a.clone();
|
||||||
for &MatrixEntry { index, value } in self {
|
for &MatrixEntry { index, value } in self {
|
||||||
|
@ -135,15 +128,6 @@ impl PartialMatrix {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Display for PartialMatrix {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> {
|
|
||||||
for &MatrixEntry { index: (row, col), value } in self {
|
|
||||||
writeln!(f, " {row} {col} {value}")?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoIterator for PartialMatrix {
|
impl IntoIterator for PartialMatrix {
|
||||||
type Item = MatrixEntry;
|
type Item = MatrixEntry;
|
||||||
type IntoIter = std::vec::IntoIter<Self::Item>;
|
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue