forked from StudioInfinity/dyna3
Add the test assemblies for the demo
These are partial setups of the tetrahedron radius ratio problem and the Irisawa hexlet problem.
This commit is contained in:
parent
eba15a6e83
commit
6928ac8765
2 changed files with 272 additions and 6 deletions
|
@ -1,11 +1,20 @@
|
||||||
use std::{f64::consts::FRAC_1_SQRT_2, rc::Rc};
|
use itertools::izip;
|
||||||
|
use nalgebra::Vector3;
|
||||||
|
use std::{f64::consts::{FRAC_1_SQRT_2, PI}, rc::Rc};
|
||||||
use sycamore::prelude::*;
|
use sycamore::prelude::*;
|
||||||
use web_sys::{console, wasm_bindgen::JsValue};
|
use web_sys::{console, wasm_bindgen::JsValue};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
engine,
|
engine,
|
||||||
AppState,
|
AppState,
|
||||||
assembly::{Assembly, InversiveDistanceRegulator, Point, Sphere}
|
assembly::{
|
||||||
|
Assembly,
|
||||||
|
ElementColor,
|
||||||
|
InversiveDistanceRegulator,
|
||||||
|
Point,
|
||||||
|
Sphere
|
||||||
|
},
|
||||||
|
specified::SpecifiedValue
|
||||||
};
|
};
|
||||||
|
|
||||||
/* DEBUG */
|
/* DEBUG */
|
||||||
|
@ -179,6 +188,241 @@ fn load_pointed_assemb(assembly: &Assembly) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// to finish setting up the problem, fix the following curvatures:
|
||||||
|
// sun 1
|
||||||
|
// moon 5/3 = 1.666666666666666...
|
||||||
|
// chain1 2
|
||||||
|
// a tiny `x` or `z` nudge of the outer sphere reliably prevents realization
|
||||||
|
// failures before they happen, or resolves them after they happen. the result
|
||||||
|
// depends sensitively on the translation direction, suggesting that realization
|
||||||
|
// is failing because the engine is having trouble breaking a symmetry
|
||||||
|
// /* TO DO */
|
||||||
|
// the engine's performance on this problem is scale-dependent! with the current
|
||||||
|
// initial conditions, realization fails for any order of imposing the remaining
|
||||||
|
// curvature constraints. scaling everything up by a factor of ten, as done in
|
||||||
|
// the original problem, makes realization succeed reliably. one potentially
|
||||||
|
// relevant difference is that a lot of the numbers in the current initial
|
||||||
|
// conditions are exactly representable as floats, unlike the analogous numbers
|
||||||
|
// in the scaled-up problem. the inexact representations might break the
|
||||||
|
// symmetry that's getting the engine stuck
|
||||||
|
fn load_irisawa_hexlet_assemb(assembly: &Assembly) {
|
||||||
|
let index_range = 1..=6;
|
||||||
|
let colors = [
|
||||||
|
[1.00_f32, 0.00_f32, 0.25_f32],
|
||||||
|
[1.00_f32, 0.25_f32, 0.00_f32],
|
||||||
|
[0.75_f32, 0.75_f32, 0.00_f32],
|
||||||
|
[0.25_f32, 1.00_f32, 0.00_f32],
|
||||||
|
[0.00_f32, 0.25_f32, 1.00_f32],
|
||||||
|
[0.25_f32, 0.00_f32, 1.00_f32]
|
||||||
|
].into_iter();
|
||||||
|
|
||||||
|
// create the spheres
|
||||||
|
let spheres = [
|
||||||
|
Sphere::new(
|
||||||
|
"outer".to_string(),
|
||||||
|
"Outer".to_string(),
|
||||||
|
[0.5_f32, 0.5_f32, 0.5_f32],
|
||||||
|
engine::sphere(0.0, 0.0, 0.0, 1.5)
|
||||||
|
),
|
||||||
|
Sphere::new(
|
||||||
|
"sun".to_string(),
|
||||||
|
"Sun".to_string(),
|
||||||
|
[0.75_f32, 0.75_f32, 0.75_f32],
|
||||||
|
engine::sphere(0.0, -0.75, 0.0, 0.75)
|
||||||
|
),
|
||||||
|
Sphere::new(
|
||||||
|
"moon".to_string(),
|
||||||
|
"Moon".to_string(),
|
||||||
|
[0.25_f32, 0.25_f32, 0.25_f32],
|
||||||
|
engine::sphere(0.0, 0.75, 0.0, 0.75)
|
||||||
|
),
|
||||||
|
].into_iter().chain(
|
||||||
|
index_range.clone().zip(colors).map(
|
||||||
|
|(k, color)| {
|
||||||
|
let ang = (k as f64) * PI/3.0;
|
||||||
|
Sphere::new(
|
||||||
|
format!("chain{k}"),
|
||||||
|
format!("Chain {k}"),
|
||||||
|
color,
|
||||||
|
engine::sphere(1.0 * ang.sin(), 0.0, 1.0 * ang.cos(), 0.5)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
);
|
||||||
|
for sphere in spheres {
|
||||||
|
let _ = assembly.try_insert_element(sphere);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fix the curvature of the outer sphere
|
||||||
|
let outer = assembly.elements_by_id.with_untracked(
|
||||||
|
|elts_by_id| elts_by_id["outer"].clone()
|
||||||
|
);
|
||||||
|
let outer_curvature_regulator = outer.regulators().with_untracked(
|
||||||
|
|regs| regs.first().unwrap().clone()
|
||||||
|
);
|
||||||
|
outer_curvature_regulator.set_point().set(
|
||||||
|
SpecifiedValue::try_from((1.0 / 3.0/*30.0*/).to_string()).unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
// impose the desired tangencies
|
||||||
|
let [outer, sun, moon] = ["outer", "sun", "moon"].map(
|
||||||
|
|id| assembly.elements_by_id.with_untracked(
|
||||||
|
|elts_by_id| elts_by_id[id].clone()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
let chain = index_range.map(
|
||||||
|
|k| assembly.elements_by_id.with_untracked(
|
||||||
|
|elts_by_id| elts_by_id[&format!("chain{k}")].clone()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
for (chain_sphere, chain_sphere_next) in chain.clone().zip(chain.cycle().skip(1)) {
|
||||||
|
for (other_sphere, inversive_distance) in [
|
||||||
|
(outer.clone(), "1"),
|
||||||
|
(sun.clone(), "-1"),
|
||||||
|
(moon.clone(), "-1"),
|
||||||
|
(chain_sphere_next.clone(), "-1")
|
||||||
|
] {
|
||||||
|
let tangency = InversiveDistanceRegulator::new([chain_sphere.clone(), other_sphere]);
|
||||||
|
tangency.set_point.set(SpecifiedValue::try_from(inversive_distance.to_string()).unwrap());
|
||||||
|
assembly.insert_regulator(Rc::new(tangency));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let outer_sun_tangency = InversiveDistanceRegulator::new([outer.clone(), sun]);
|
||||||
|
outer_sun_tangency.set_point.set(SpecifiedValue::try_from("1".to_string()).unwrap());
|
||||||
|
assembly.insert_regulator(Rc::new(outer_sun_tangency));
|
||||||
|
|
||||||
|
let outer_moon_tangency = InversiveDistanceRegulator::new([outer.clone(), moon]);
|
||||||
|
outer_moon_tangency.set_point.set(SpecifiedValue::try_from("1".to_string()).unwrap());
|
||||||
|
assembly.insert_regulator(Rc::new(outer_moon_tangency));
|
||||||
|
}
|
||||||
|
|
||||||
|
// setting the inversive distances between the vertices to -2 gives a
|
||||||
|
// regular tetrahedron with side length 1, whose insphere and circumsphere have
|
||||||
|
// radii sqrt(1/6) and sqrt(3/2), respectively
|
||||||
|
fn load_radius_ratio_assemb(assembly: &Assembly) {
|
||||||
|
let index_range = 1..=4;
|
||||||
|
|
||||||
|
// create the spheres
|
||||||
|
const GRAY: ElementColor = [0.75_f32, 0.75_f32, 0.75_f32];
|
||||||
|
let spheres = [
|
||||||
|
Sphere::new(
|
||||||
|
"sphere_faces".to_string(),
|
||||||
|
"Insphere".to_string(),
|
||||||
|
GRAY,
|
||||||
|
engine::sphere(0.0, 0.0, 0.0, 0.5)
|
||||||
|
),
|
||||||
|
Sphere::new(
|
||||||
|
"sphere_vertices".to_string(),
|
||||||
|
"Circumsphere".to_string(),
|
||||||
|
GRAY,
|
||||||
|
engine::sphere(0.0, 0.0, 0.0, 0.25)
|
||||||
|
)
|
||||||
|
];
|
||||||
|
for sphere in spheres {
|
||||||
|
let _ = assembly.try_insert_element(sphere);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the vertices
|
||||||
|
let vertices = izip!(
|
||||||
|
index_range.clone(),
|
||||||
|
[
|
||||||
|
[1.00_f32, 0.50_f32, 0.75_f32],
|
||||||
|
[1.00_f32, 0.75_f32, 0.50_f32],
|
||||||
|
[1.00_f32, 1.00_f32, 0.50_f32],
|
||||||
|
[0.75_f32, 0.50_f32, 1.00_f32]
|
||||||
|
].into_iter(),
|
||||||
|
[
|
||||||
|
engine::point(-0.6, -0.8, -0.6),
|
||||||
|
engine::point(-0.6, 0.8, 0.6),
|
||||||
|
engine::point(0.6, -0.8, 0.6),
|
||||||
|
engine::point(0.6, 0.8, -0.6)
|
||||||
|
].into_iter()
|
||||||
|
).map(
|
||||||
|
|(k, color, representation)| {
|
||||||
|
Point::new(
|
||||||
|
format!("v{k}"),
|
||||||
|
format!("Vertex {k}"),
|
||||||
|
color,
|
||||||
|
representation
|
||||||
|
)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
for vertex in vertices {
|
||||||
|
let _ = assembly.try_insert_element(vertex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// create the faces
|
||||||
|
let base_dir = Vector3::new(1.0, 0.75, 1.0).normalize();
|
||||||
|
let offset = base_dir.dot(&Vector3::new(-0.6, 0.8, 0.6));
|
||||||
|
let faces =izip!(
|
||||||
|
index_range.clone(),
|
||||||
|
[
|
||||||
|
[1.00_f32, 0.00_f32, 0.25_f32],
|
||||||
|
[1.00_f32, 0.25_f32, 0.00_f32],
|
||||||
|
[0.75_f32, 0.75_f32, 0.00_f32],
|
||||||
|
[0.25_f32, 0.00_f32, 1.00_f32]
|
||||||
|
].into_iter(),
|
||||||
|
[
|
||||||
|
engine::sphere_with_offset(base_dir[0], base_dir[1], base_dir[2], offset, 0.0),
|
||||||
|
engine::sphere_with_offset(base_dir[0], -base_dir[1], -base_dir[2], offset, 0.0),
|
||||||
|
engine::sphere_with_offset(-base_dir[0], base_dir[1], -base_dir[2], offset, 0.0),
|
||||||
|
engine::sphere_with_offset(-base_dir[0], -base_dir[1], base_dir[2], offset, 0.0)
|
||||||
|
].into_iter()
|
||||||
|
).map(
|
||||||
|
|(k, color, representation)| {
|
||||||
|
Sphere::new(
|
||||||
|
format!("f{k}"),
|
||||||
|
format!("Face {k}"),
|
||||||
|
color,
|
||||||
|
representation
|
||||||
|
)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
for face in faces {
|
||||||
|
let _ = assembly.try_insert_element(face);
|
||||||
|
}
|
||||||
|
|
||||||
|
// impose the constraints
|
||||||
|
for j in index_range.clone() {
|
||||||
|
let [face_j, vertex_j] = [
|
||||||
|
format!("f{j}"),
|
||||||
|
format!("v{j}")
|
||||||
|
].map(
|
||||||
|
|id| assembly.elements_by_id.with_untracked(
|
||||||
|
|elts_by_id| elts_by_id[&id].clone()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
// make the faces planar
|
||||||
|
let curvature_regulator = face_j.regulators().with_untracked(
|
||||||
|
|regs| regs.first().unwrap().clone()
|
||||||
|
);
|
||||||
|
curvature_regulator.set_point().set(
|
||||||
|
SpecifiedValue::try_from("0".to_string()).unwrap()
|
||||||
|
);
|
||||||
|
|
||||||
|
for k in index_range.clone().filter(|&index| index != j) {
|
||||||
|
let vertex_k = assembly.elements_by_id.with_untracked(
|
||||||
|
|elts_by_id| elts_by_id[&format!("v{k}")].clone()
|
||||||
|
);
|
||||||
|
|
||||||
|
// fix the distances between the vertices
|
||||||
|
if j < k {
|
||||||
|
let distance_regulator = InversiveDistanceRegulator::new(
|
||||||
|
[vertex_j.clone(), vertex_k.clone()]
|
||||||
|
);
|
||||||
|
assembly.insert_regulator(Rc::new(distance_regulator));
|
||||||
|
}
|
||||||
|
|
||||||
|
// put the vertices on the faces
|
||||||
|
let incidence_regulator = InversiveDistanceRegulator::new([face_j.clone(), vertex_k.clone()]);
|
||||||
|
incidence_regulator.set_point.set(SpecifiedValue::try_from("0".to_string()).unwrap());
|
||||||
|
assembly.insert_regulator(Rc::new(incidence_regulator));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn AddRemove() -> View {
|
pub fn AddRemove() -> View {
|
||||||
/* DEBUG */
|
/* DEBUG */
|
||||||
|
@ -205,6 +449,8 @@ pub fn AddRemove() -> View {
|
||||||
"general" => load_gen_assemb(assembly),
|
"general" => load_gen_assemb(assembly),
|
||||||
"low-curv" => load_low_curv_assemb(assembly),
|
"low-curv" => load_low_curv_assemb(assembly),
|
||||||
"pointed" => load_pointed_assemb(assembly),
|
"pointed" => load_pointed_assemb(assembly),
|
||||||
|
"irisawa-hexlet" => load_irisawa_hexlet_assemb(assembly),
|
||||||
|
"radius-ratio" => load_radius_ratio_assemb(assembly),
|
||||||
_ => ()
|
_ => ()
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
@ -253,6 +499,8 @@ pub fn AddRemove() -> View {
|
||||||
option(value="general") { "General" }
|
option(value="general") { "General" }
|
||||||
option(value="low-curv") { "Low-curvature" }
|
option(value="low-curv") { "Low-curvature" }
|
||||||
option(value="pointed") { "Pointed" }
|
option(value="pointed") { "Pointed" }
|
||||||
|
option(value="irisawa-hexlet") { "Irisawa hexlet" }
|
||||||
|
option(value="radius-ratio") { "Radius ratio" }
|
||||||
option(value="empty") { "Empty" }
|
option(value="empty") { "Empty" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -534,6 +534,9 @@ pub struct Assembly {
|
||||||
pub elements: Signal<BTreeSet<Rc<dyn Element>>>,
|
pub elements: Signal<BTreeSet<Rc<dyn Element>>>,
|
||||||
pub regulators: Signal<BTreeSet<Rc<dyn Regulator>>>,
|
pub regulators: Signal<BTreeSet<Rc<dyn Regulator>>>,
|
||||||
|
|
||||||
|
// a flag that tells us whether the assembly is realized
|
||||||
|
pub realized: Signal<bool>,
|
||||||
|
|
||||||
// solution variety tangent space. the basis vectors are stored in
|
// solution variety tangent space. the basis vectors are stored in
|
||||||
// configuration matrix format, ordered according to the elements' column
|
// configuration matrix format, ordered according to the elements' column
|
||||||
// indices. when you realize the assembly, every element that's present
|
// indices. when you realize the assembly, every element that's present
|
||||||
|
@ -552,12 +555,24 @@ pub struct Assembly {
|
||||||
|
|
||||||
impl Assembly {
|
impl Assembly {
|
||||||
pub fn new() -> Assembly {
|
pub fn new() -> Assembly {
|
||||||
Assembly {
|
// create an assembly
|
||||||
|
let assembly = Assembly {
|
||||||
elements: create_signal(BTreeSet::new()),
|
elements: create_signal(BTreeSet::new()),
|
||||||
regulators: create_signal(BTreeSet::new()),
|
regulators: create_signal(BTreeSet::new()),
|
||||||
|
realized: create_signal(true),
|
||||||
tangent: create_signal(ConfigSubspace::zero(0)),
|
tangent: create_signal(ConfigSubspace::zero(0)),
|
||||||
elements_by_id: create_signal(BTreeMap::default())
|
elements_by_id: create_signal(BTreeMap::default())
|
||||||
|
};
|
||||||
|
|
||||||
|
// realize the assembly whenever the `realized` flag is set to `false`
|
||||||
|
let assembly_for_effect = assembly.clone();
|
||||||
|
create_memo(move || {
|
||||||
|
if !assembly_for_effect.realized.get() {
|
||||||
|
assembly_for_effect.realize();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
assembly
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- inserting elements and regulators ---
|
// --- inserting elements and regulators ---
|
||||||
|
@ -627,7 +642,7 @@ impl Assembly {
|
||||||
console_log!("Updated regulator with subjects {:?}", regulator.subjects());
|
console_log!("Updated regulator with subjects {:?}", regulator.subjects());
|
||||||
|
|
||||||
if regulator.try_activate() {
|
if regulator.try_activate() {
|
||||||
self_for_effect.realize();
|
self_for_effect.realized.set(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -710,6 +725,9 @@ impl Assembly {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// update the realization flag
|
||||||
|
self.realized.set(true);
|
||||||
|
|
||||||
// save the tangent space
|
// save the tangent space
|
||||||
self.tangent.set_silent(tangent);
|
self.tangent.set_silent(tangent);
|
||||||
}
|
}
|
||||||
|
@ -802,7 +820,7 @@ impl Assembly {
|
||||||
// bring the configuration back onto the solution variety. this also
|
// bring the configuration back onto the solution variety. this also
|
||||||
// gets the elements' column indices and the saved tangent space back in
|
// gets the elements' column indices and the saved tangent space back in
|
||||||
// sync
|
// sync
|
||||||
self.realize();
|
self.realized.set(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue