Try using pinned vertices as the hard constraints

This commit is contained in:
Aaron Fenyes 2025-09-22 16:56:13 -07:00
parent 476b13c87f
commit 096a153f04
2 changed files with 518 additions and 4 deletions

View file

@ -1,6 +1,7 @@
use enum_iterator::{all, Sequence};
use nalgebra::{DMatrix, DVector, DVectorView};
use std::{
any::Any,
cell::Cell,
cmp::Ordering,
collections::{BTreeMap, BTreeSet},
@ -375,7 +376,7 @@ impl ProblemPoser for Point {
}
}
pub trait Regulator: Serial + ProblemPoser + OutlineItem {
pub trait Regulator: Any + Serial + ProblemPoser + OutlineItem {
fn subjects(&self) -> Vec<Rc<dyn Element>>;
fn measurement(&self) -> ReadSignal<f64>;
fn set_point(&self) -> Signal<SpecifiedValue>;
@ -385,6 +386,7 @@ pub trait Regulator: Serial + ProblemPoser + OutlineItem {
fn distortion(&self) -> Option<ReadSignal<f64>> { /* KLUDGE */
None
}
fn as_any(&self) -> &dyn Any;
}
impl Hash for dyn Regulator {
@ -475,6 +477,10 @@ impl Regulator for InversiveDistanceRegulator {
fn distortion(&self) -> Option<ReadSignal<f64>> {
self.distortion
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl Serial for InversiveDistanceRegulator {
@ -535,6 +541,10 @@ impl Regulator for HalfCurvatureRegulator {
fn set_point(&self) -> Signal<SpecifiedValue> {
self.set_point
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl Serial for HalfCurvatureRegulator {
@ -587,9 +597,21 @@ impl Serial for PointCoordinateRegulator {
}
impl Regulator for PointCoordinateRegulator {
fn subjects(&self) -> Vec<Rc<dyn Element>> { vec![self.subject.clone()] }
fn measurement(&self) -> ReadSignal<f64> { self.measurement }
fn set_point(&self) -> Signal<SpecifiedValue> { self.set_point }
fn subjects(&self) -> Vec<Rc<dyn Element>> {
vec![self.subject.clone()]
}
fn measurement(&self) -> ReadSignal<f64> {
self.measurement
}
fn set_point(&self) -> Signal<SpecifiedValue> {
self.set_point
}
fn as_any(&self) -> &dyn Any {
self
}
}
impl ProblemPoser for PointCoordinateRegulator {

View file

@ -12,6 +12,8 @@ use crate::{
ElementColor,
InversiveDistanceRegulator,
Point,
PointCoordinateRegulator,
Regulator,
Sphere,
},
engine,
@ -3144,6 +3146,494 @@ fn load_554_domed(assembly: &Assembly) {
}
}
fn load_554_domed_pinned(assembly: &Assembly) {
// create the vertices
const COLOR_A: ElementColor = [0.75_f32, 0.00_f32, 0.75_f32];
const COLOR_Z: ElementColor = [1.00_f32, 0.40_f32, 0.60_f32];
const COLOR_B: ElementColor = [1.00_f32, 0.00_f32, 0.25_f32];
const COLOR_Y: ElementColor = [1.00_f32, 0.75_f32, 0.25_f32];
const COLOR_C: ElementColor = [0.75_f32, 0.50_f32, 0.00_f32];
const COLOR_D: ElementColor = [0.90_f32, 0.90_f32, 0.00_f32];
const COLOR_E: ElementColor = [0.25_f32, 0.75_f32, 0.00_f32];
const COLOR_F: ElementColor = [0.00_f32, 0.50_f32, 0.75_f32];
const COLOR_G: ElementColor = [0.25_f32, 0.00_f32, 1.00_f32];
const COLOR_H: ElementColor = COLOR_A;
const COLOR_I: ElementColor = COLOR_B;
const COLOR_J: ElementColor = COLOR_C;
let vertices = [
Point::new(
"a_NE".to_string(),
"A-NE".to_string(),
COLOR_A,
engine::point(0.5, 0.5, 0.0),
),
Point::new(
"a_NW".to_string(),
"A-NW".to_string(),
COLOR_A,
engine::point(-0.5, 0.5, 0.0),
),
Point::new(
"a_SW".to_string(),
"A-SW".to_string(),
COLOR_A,
engine::point(-0.5, -0.5, 0.0),
),
Point::new(
"a_SE".to_string(),
"A-SE".to_string(),
COLOR_A,
engine::point(0.5, -0.5, 0.0),
),
Point::new(
"z_S".to_string(),
"Z-S".to_string(),
COLOR_Z,
engine::point(0.0, -0.4, 0.6),
),
Point::new(
"z_E".to_string(),
"Z-E".to_string(),
COLOR_Z,
engine::point(0.4, 0.0, 0.6),
),
Point::new(
"b_NE".to_string(),
"B-NE".to_string(),
COLOR_B,
engine::point(0.7, 0.7, 0.9),
),
Point::new(
"b_NW".to_string(),
"B-NW".to_string(),
COLOR_B,
engine::point(-0.7, 0.7, 0.9),
),
Point::new(
"b_SW".to_string(),
"B-SW".to_string(),
COLOR_B,
engine::point(-0.7, -0.7, 0.9),
),
Point::new(
"b_SE".to_string(),
"B-SE".to_string(),
COLOR_B,
engine::point(0.7, -0.7, 0.9),
),
Point::new(
"y_NE".to_string(),
"Y-NE".to_string(),
COLOR_Y,
engine::point(0.1, 0.1, 1.0),
),
Point::new(
"y_NW".to_string(),
"Y-NW".to_string(),
COLOR_Y,
engine::point(-0.1, 0.1, 1.0),
),
Point::new(
"y_SW".to_string(),
"Y-SW".to_string(),
COLOR_Y,
engine::point(-0.1, -0.1, 1.0),
),
Point::new(
"y_SE".to_string(),
"Y-SE".to_string(),
COLOR_Y,
engine::point(0.1, -0.1, 1.0),
),
Point::new(
"c_N".to_string(),
"C-N".to_string(),
COLOR_C,
engine::point(0.0, 0.8, 1.4),
),
Point::new(
"c_W".to_string(),
"C-W".to_string(),
COLOR_C,
engine::point(-0.8, 0.0, 1.4),
),
Point::new(
"c_S".to_string(),
"C-S".to_string(),
COLOR_C,
engine::point(0.0, -0.8, 1.4),
),
Point::new(
"c_E".to_string(),
"C-E".to_string(),
COLOR_C,
engine::point(0.8, 0.0, 1.4),
),
Point::new(
"d_NE".to_string(),
"D-NE".to_string(),
COLOR_D,
engine::point(0.1, 0.1, 1.8),
),
Point::new(
"d_NW".to_string(),
"D-NW".to_string(),
COLOR_D,
engine::point(-0.1, 0.1, 1.8),
),
Point::new(
"d_SW".to_string(),
"D-SW".to_string(),
COLOR_D,
engine::point(-0.1, -0.1, 1.8),
),
Point::new(
"d_SE".to_string(),
"D-SE".to_string(),
COLOR_D,
engine::point(0.1, -0.1, 1.8),
),
Point::new(
"e_N".to_string(),
"E-N".to_string(),
COLOR_E,
engine::point(0.0, 0.7, 2.3),
),
Point::new(
"e_W".to_string(),
"E-W".to_string(),
COLOR_E,
engine::point(-0.7, 0.0, 2.3),
),
Point::new(
"e_S".to_string(),
"E-S".to_string(),
COLOR_E,
engine::point(0.0, -0.7, 2.3),
),
Point::new(
"e_E".to_string(),
"E-E".to_string(),
COLOR_E,
engine::point(0.7, 0.0, 2.3),
),
Point::new(
"f_NE".to_string(),
"F-NE".to_string(),
COLOR_F,
engine::point(0.2, 0.2, 2.7),
),
Point::new(
"f_NW".to_string(),
"F-NW".to_string(),
COLOR_F,
engine::point(-0.2, 0.2, 2.7),
),
Point::new(
"f_SW".to_string(),
"F-SW".to_string(),
COLOR_F,
engine::point(-0.2, -0.2, 2.7),
),
Point::new(
"f_SE".to_string(),
"F-SE".to_string(),
COLOR_F,
engine::point(0.2, -0.2, 2.7),
),
Point::new(
"g_NNE".to_string(),
"G-NNE".to_string(),
COLOR_G,
engine::point(0.5, 1.2, 3.0),
),
Point::new(
"g_NNW".to_string(),
"G-NNW".to_string(),
COLOR_G,
engine::point(-0.5, 1.2, 3.0),
),
Point::new(
"g_WNW".to_string(),
"G-WNW".to_string(),
COLOR_G,
engine::point(-1.2, 0.5, 3.0),
),
Point::new(
"g_WSW".to_string(),
"G-WSW".to_string(),
COLOR_G,
engine::point(-1.2, -0.5, 3.0),
),
Point::new(
"g_SSW".to_string(),
"G-SSW".to_string(),
COLOR_G,
engine::point(-0.5, -1.2, 3.0),
),
Point::new(
"g_SSE".to_string(),
"G-SSE".to_string(),
COLOR_G,
engine::point(0.5, -1.2, 3.0),
),
Point::new(
"g_ESE".to_string(),
"G-ESE".to_string(),
COLOR_G,
engine::point(1.2, -0.5, 3.0),
),
Point::new(
"g_ENE".to_string(),
"G-ENE".to_string(),
COLOR_G,
engine::point(1.2, 0.5, 3.0),
),
Point::new(
"h_N".to_string(),
"H-N".to_string(),
COLOR_H,
engine::point(0.0, 0.6, 2.9),
),
Point::new(
"h_W".to_string(),
"H-W".to_string(),
COLOR_H,
engine::point(-0.6, 0.0, 2.9),
),
Point::new(
"h_S".to_string(),
"H-S".to_string(),
COLOR_H,
engine::point(0.0, -0.6, 2.9),
),
Point::new(
"h_E".to_string(),
"H-E".to_string(),
COLOR_H,
engine::point(0.6, 0.0, 2.9),
),
Point::new(
"i_NE".to_string(),
"I-NE".to_string(),
COLOR_I,
engine::point(0.5, 0.5, 3.5),
),
Point::new(
"i_NW".to_string(),
"I-NW".to_string(),
COLOR_I,
engine::point(-0.5, 0.5, 3.5),
),
Point::new(
"i_SW".to_string(),
"I-SW".to_string(),
COLOR_I,
engine::point(-0.5, -0.5, 3.5),
),
Point::new(
"i_SE".to_string(),
"I-SE".to_string(),
COLOR_I,
engine::point(0.5, -0.5, 3.5),
),
Point::new(
"j".to_string(),
"J".to_string(),
COLOR_J,
engine::point(0.0, 0.0, 3.0),
),
];
for vertex in vertices {
let _ = assembly.try_insert_element(vertex);
}
// fix the distances between adjacent vertices
let struts: Vec<_> = vec![
(false, 1.0, vec![
["a_SE", "b_SE"],
["b_SW", "c_S"],
["b_SE", "c_S"],
["b_SE", "c_E"],
["b_NE", "c_E"],
["z_S", "a_SW"],
["z_S", "a_SE"],
["z_E", "a_SE"],
["z_E", "a_NE"],
["z_S", "b_SW"],
["z_S", "b_SE"],
["z_E", "b_SE"],
["z_E", "b_NE"],
["z_S", "c_S"],
["z_E", "c_E"],
["c_N", "d_NE"],
["c_N", "d_NW"],
["c_W", "d_NW"],
["c_W", "d_SW"],
["c_S", "d_SW"],
["c_S", "d_SE"],
["c_E", "d_SE"],
["c_E", "d_NE"],
["y_NE", "b_NE"],
["y_NW", "b_NW"],
["y_SW", "b_SW"],
["y_SE", "b_SE"],
["y_NE", "c_N"],
["y_NW", "c_N"],
["y_NW", "c_W"],
["y_SW", "c_W"],
["y_SW", "c_S"],
["y_SE", "c_S"],
["y_SE", "c_E"],
["y_NE", "c_E"],
["y_NE", "d_NE"],
["y_NW", "d_NW"],
["y_SW", "d_SW"],
["y_SE", "d_SE"],
["d_NE", "e_N"],
["d_NW", "e_N"],
["d_NW", "e_W"],
["d_SW", "e_W"],
["d_SW", "e_S"],
["d_SE", "e_S"],
["d_SE", "e_E"],
["d_NE", "e_E"],
["c_N", "e_N"],
["c_W", "e_W"],
["c_S", "e_S"],
["c_E", "e_E"],
["e_N", "f_NE"],
["e_N", "f_NW"],
["e_W", "f_NW"],
["e_W", "f_SW"],
["e_S", "f_SW"],
["e_S", "f_SE"],
["e_E", "f_SE"],
["e_E", "f_NE"],
["d_NE", "f_NE"],
["d_NW", "f_NW"],
["d_SW", "f_SW"],
["d_SE", "f_SE"],
["f_NE", "g_ENE"],
["f_NE", "g_NNE"],
["f_NW", "g_NNW"],
["f_NW", "g_WNW"],
["f_SW", "g_WSW"],
["f_SW", "g_SSW"],
["f_SE", "g_SSE"],
["f_SE", "g_ESE"],
["e_N", "g_NNE"],
["e_N", "g_NNW"],
["e_W", "g_WNW"],
["e_W", "g_WSW"],
["e_S", "g_SSW"],
["e_S", "g_SSE"],
["e_E", "g_ESE"],
["e_E", "g_ENE"],
["g_NNE", "g_NNW"],
["g_NNW", "g_WNW"],
["g_WNW", "g_WSW"],
["g_WSW", "g_SSW"],
["g_SSW", "g_SSE"],
["g_SSE", "g_ESE"],
["g_ESE", "g_ENE"],
["g_ENE", "g_NNE"],
["g_NNE", "h_N"],
["g_NNW", "h_N"],
["g_WNW", "h_W"],
["g_WSW", "h_W"],
["g_SSW", "h_S"],
["g_SSE", "h_S"],
["g_ESE", "h_E"],
["g_ENE", "h_E"],
["h_N", "i_NE"],
["h_N", "i_NW"],
["h_W", "i_NW"],
["h_W", "i_SW"],
["h_S", "i_SW"],
["h_S", "i_SE"],
["h_E", "i_SE"],
["h_E", "i_NE"],
["g_NNE", "i_NE"],
["g_NNW", "i_NW"],
["g_WNW", "i_NW"],
["g_WSW", "i_SW"],
["g_SSW", "i_SW"],
["g_SSE", "i_SE"],
["g_ESE", "i_SE"],
["g_ENE", "i_NE"],
["i_NE", "i_NW"],
["i_NW", "i_SW"],
["i_SW", "i_SE"],
["i_SE", "i_NE"],
["i_NE", "j"],
["i_NW", "j"],
["i_SW", "j"],
["i_SE", "j"],
]),
];
for (soft, length, vertex_pairs) in struts {
let inv_dist = Some(-0.5 * length * length);
for pair in vertex_pairs {
let adjacent_vertices = pair.map(
|id| assembly.elements_by_id.with_untracked(
|elts_by_id| elts_by_id[id].clone()
)
);
let distance = InversiveDistanceRegulator::new(adjacent_vertices);
distance.set_point.set(SpecifiedValue::from(inv_dist));
distance.soft.set(soft);
assembly.insert_regulator(Rc::new(distance));
}
}
// pin the vertices of the rigid faces
let phi = 0.5 + 1.25_f64.sqrt(); /* TO DO */ // replace with std::f64::consts::PHI when that gets stabilized
let phi_2 = 0.5 * phi;
let height_b = phi_2.sqrt();
let height_c = (phi + 0.5).sqrt();
let pinned_vertices = [
("a_NE", [0.5, 0.5, 0.0]),
("a_NW", [-0.5, 0.5, 0.0]),
("a_SW", [-0.5, -0.5, 0.0]),
("a_SE", [0.5, -0.5, 0.0]),
("b_NE", [phi_2, phi_2, height_b]),
("b_NW", [-phi_2, phi_2, height_b]),
("b_SW", [-phi_2, -phi_2, height_b]),
("c_N", [0.0, 1.0, height_c]),
("c_W", [-1.0, 0.0, height_c]),
];
for (id, coords) in pinned_vertices {
// get the point's coordinate regulators
let point = assembly.elements_by_id.with_untracked(
|elts_by_id| elts_by_id[id].clone()
);
let coord_regs: Vec<Rc<dyn Regulator>> = point.regulators().with_untracked(
|regs| regs.into_iter().filter_map(
/* KLUDGE */
// there must be a better way to do the type-checking here
move |reg| reg
.as_any()
.downcast_ref::<PointCoordinateRegulator>()
.map(|_| reg.clone())
).collect()
);
// set the coordinates
for reg in coord_regs {
let reg_downcast_ref = reg
.as_any()
.downcast_ref::<PointCoordinateRegulator>();
if let Some(coord_reg) = reg_downcast_ref {
let coord_val = SpecifiedValue::from(
Some(coords[coord_reg.axis as usize])
);
reg.set_point().set(coord_val);
}
}
}
}
// --- chooser ---
/* DEBUG */
@ -3185,6 +3675,7 @@ pub fn TestAssemblyChooser() -> View {
"554-aug1-inner" => load_554_aug1_inner(assembly),
"554-aug2" => load_554_aug2(assembly),
"554-domed" => load_554_domed(assembly),
"554-domed-pinned" => load_554_domed_pinned(assembly),
_ => (),
};
});
@ -3207,6 +3698,7 @@ pub fn TestAssemblyChooser() -> View {
option(value = "554-aug1-inner") { "5-5-4 once augmented (inner)" }
option(value = "554-aug2") { "5-5-4 twice augmented" }
option(value = "554-domed") { "5-5-4 domed" }
option(value = "554-domed-pinned") { "5-5-4 domed (pinned)" }
option(value = "empty") { "Empty" }
}
}