diff --git a/app-proto/Cargo.toml b/app-proto/Cargo.toml index 844a0a6..5d97193 100644 --- a/app-proto/Cargo.toml +++ b/app-proto/Cargo.toml @@ -46,6 +46,9 @@ features = [ dyna3 = { path = ".", default-features = false, features = ["dev"] } wasm-bindgen-test = "0.3.34" +[lints.rust] +unexpected_cfgs = { level = "warn", check-cfg = ["cfg(sycamore_force_ssr)"] } + [profile.release] opt-level = "s" # optimize for small code size debug = true # include debug symbols diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index 0aa6dab..2a23b4a 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -623,9 +623,7 @@ impl Assembly { create_effect(move || { /* DEBUG */ // log the regulator update - console::log_1(&JsValue::from( - format!("Updated regulator with subjects {:?}", regulator.subjects()) - )); + console_log!("Updated regulator with subjects {:?}", regulator.subjects()); if regulator.try_activate() { self_for_effect.realize(); @@ -634,10 +632,10 @@ impl Assembly { /* DEBUG */ // print an updated list of regulators - console::log_1(&JsValue::from("Regulators:")); + console_log!("Regulators:"); self.regulators.with_untracked(|regs| { for reg in regs.into_iter() { - console::log_1(&JsValue::from(format!( + console_log!( " {:?}: {}", reg.subjects(), reg.set_point().with_untracked( @@ -650,7 +648,7 @@ impl Assembly { } } ) - ))); + ); } }); } @@ -681,19 +679,11 @@ impl Assembly { /* DEBUG */ // log the Gram matrix - console::log_1(&JsValue::from("Gram matrix:")); - problem.gram.log_to_console(); + console_log!("Gram matrix:\n{}", problem.gram); /* DEBUG */ // log the initial configuration matrix - 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)); - } + console_log!("Old configuration:{:>8.3}", problem.guess); // look for a configuration with the given Gram matrix let (config, tangent, success, history) = realize_gram( @@ -702,16 +692,14 @@ impl Assembly { /* DEBUG */ // report the outcome of the search - console::log_1(&JsValue::from( - if success { - "Target accuracy achieved!" - } else { - "Failed to reach target accuracy" - } - )); - console::log_2(&JsValue::from("Steps:"), &JsValue::from(history.scaled_loss.len() - 1)); - 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 { + console_log!("Target accuracy achieved!") + } else { + console_log!("Failed to reach target accuracy") + } + console_log!("Steps: {}", history.scaled_loss.len() - 1); + console_log!("Loss: {}", *history.scaled_loss.last().unwrap()); + console_log!("Tangent dimension: {}", tangent.dim()); if success { // read out the solution @@ -804,9 +792,7 @@ impl Assembly { elt.normalize_mut_rep(rep); }, None => { - console::log_1(&JsValue::from( - format!("No velocity to unpack for fresh element \"{}\"", elt.id()) - )) + console_log!("No velocity to unpack for fresh element \"{}\"", elt.id()) } }; }); @@ -823,6 +809,8 @@ impl Assembly { mod tests { use super::*; + use crate::engine; + #[test] #[should_panic(expected = "Sphere \"sphere\" should be indexed before writing problem data")] fn unindexed_element_test() { @@ -848,4 +836,50 @@ mod tests { }.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); + }); + } } \ No newline at end of file diff --git a/app-proto/src/engine.rs b/app-proto/src/engine.rs index 7bc2bdc..03d13a9 100644 --- a/app-proto/src/engine.rs +++ b/app-proto/src/engine.rs @@ -1,5 +1,6 @@ use lazy_static::lazy_static; use nalgebra::{Const, DMatrix, DVector, DVectorView, Dyn, SymmetricEigen}; +use std::fmt::{Display, Error, Formatter}; use web_sys::{console, wasm_bindgen::JsValue}; /* DEBUG */ // --- elements --- @@ -109,15 +110,6 @@ 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) -> DMatrix { let mut result = a.clone(); for &MatrixEntry { index, value } in self { @@ -143,6 +135,15 @@ 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 { type Item = MatrixEntry; type IntoIter = std::vec::IntoIter;