From 4e5cd6e4a434d185837a8519e399857a71000a9a Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Mon, 14 Jul 2025 23:18:35 -0700 Subject: [PATCH] Add a dodecahedral circle packing test assembly --- app-proto/src/add_remove.rs | 143 ++++++++++++++++++++++++++++++++++++ 1 file changed, 143 insertions(+) diff --git a/app-proto/src/add_remove.rs b/app-proto/src/add_remove.rs index cac7aa9..fbe37c2 100644 --- a/app-proto/src/add_remove.rs +++ b/app-proto/src/add_remove.rs @@ -406,6 +406,147 @@ fn load_tridim_icosahedron_assemb(assembly: &Assembly) { } } +// to finish describing the dodecahedral circle packing, set the inversive +// distance regulators to -1. some of the regulators have already been set +fn load_dodeca_packing_assemb(assembly: &Assembly) { + // add the substrate + let _ = assembly.try_insert_element( + Sphere::new( + "substrate".to_string(), + "Substrate".to_string(), + [0.75_f32, 0.75_f32, 0.75_f32], + engine::sphere(0.0, 0.0, 0.0, 1.0) + ) + ); + let substrate = assembly.elements_by_id.with_untracked( + |elts_by_id| elts_by_id["substrate"].clone() + ); + + // fix the substrate's curvature + substrate.regulators().with_untracked( + |regs| regs.first().unwrap().clone() + ).set_point().set( + SpecifiedValue::try_from("0.5".to_string()).unwrap() + ); + + // add the circles to be packed + const COLOR_A: ElementColor = [1.00_f32, 0.25_f32, 0.00_f32]; + const COLOR_B: ElementColor = [1.00_f32, 0.00_f32, 0.25_f32]; + const COLOR_C: ElementColor = [0.25_f32, 0.00_f32, 1.00_f32]; + let phi = 0.5 + 1.25_f64.sqrt(); /* TO DO */ // replace with std::f64::consts::PHI when that gets stabilized + let phi_inv = 1.0 / phi; + let coord_scale = (phi + 2.0).sqrt(); + let face_scales = [phi_inv, (13.0 / 12.0) / coord_scale]; + let face_radii = [phi_inv, 5.0 / 12.0]; + let mut faces = Vec::>::new(); + let subscripts = ["₀", "₁"]; + for j in 0..2 { + for k in 0..2 { + let small_coord = face_scales[k] * (2.0*(j as f64) - 1.0); + let big_coord = face_scales[k] * (2.0*(k as f64) - 1.0) * phi; + + let id_num = format!("{j}{k}"); + let label_sub = format!("{}{}", subscripts[j], subscripts[k]); + + // add the A face + let id_a = format!("a{id_num}"); + let _ = assembly.try_insert_element( + Sphere::new( + id_a.clone(), + format!("A{label_sub}"), + COLOR_A, + engine::sphere(0.0, small_coord, big_coord, face_radii[k]) + ) + ); + faces.push( + assembly.elements_by_id.with_untracked( + |elts_by_id| elts_by_id[&id_a].clone() + ) + ); + + // add the B face + let id_b = format!("b{id_num}"); + let _ = assembly.try_insert_element( + Sphere::new( + id_b.clone(), + format!("B{label_sub}"), + COLOR_B, + engine::sphere(small_coord, big_coord, 0.0, face_radii[k]) + ) + ); + faces.push( + assembly.elements_by_id.with_untracked( + |elts_by_id| elts_by_id[&id_b].clone() + ) + ); + + // add the C face + let id_c = format!("c{id_num}"); + let _ = assembly.try_insert_element( + Sphere::new( + id_c.clone(), + format!("C{label_sub}"), + COLOR_C, + engine::sphere(big_coord, 0.0, small_coord, face_radii[k]) + ) + ); + faces.push( + assembly.elements_by_id.with_untracked( + |elts_by_id| elts_by_id[&id_c].clone() + ) + ); + } + } + + // make each face sphere perpendicular to the substrate + for face in faces { + let right_angle = InversiveDistanceRegulator::new([face, substrate.clone()]); + right_angle.set_point.set(SpecifiedValue::try_from("0".to_string()).unwrap()); + assembly.insert_regulator(Rc::new(right_angle)); + } + + // set up the tangencies that define the packing + for [long_edge_plane, short_edge_plane] in [["a", "b"], ["b", "c"], ["c", "a"]] { + for k in 0..2 { + let long_edge_ids = [ + format!("{long_edge_plane}{k}0"), + format!("{long_edge_plane}{k}1") + ]; + let short_edge_ids = [ + format!("{short_edge_plane}0{k}"), + format!("{short_edge_plane}1{k}") + ]; + let [long_edge, short_edge] = [long_edge_ids, short_edge_ids].map( + |edge_ids| edge_ids.map( + |id| assembly.elements_by_id.with_untracked( + |elts_by_id| elts_by_id[&id].clone() + ) + ) + ); + + // set up the short-edge tangency + let short_tangency = InversiveDistanceRegulator::new(short_edge.clone()); + if k == 0 { + short_tangency.set_point.set(SpecifiedValue::try_from("-1".to_string()).unwrap()); + } + assembly.insert_regulator(Rc::new(short_tangency)); + + // set up the side tangencies + for i in 0..2 { + for j in 0..2 { + let side_tangency = InversiveDistanceRegulator::new( + [long_edge[i].clone(), short_edge[j].clone()] + ); + if i == 0 && k == 0 { + side_tangency.set_point.set(SpecifiedValue::try_from("-1".to_string()).unwrap()); + } + assembly.insert_regulator(Rc::new(side_tangency)); + } + } + } + } +} + /* DEBUG */ // the initial configuration of this test assembly deliberately violates the // constraints, so loading the assembly will trigger a non-trivial realization @@ -773,6 +914,7 @@ pub fn AddRemove() -> View { "low-curv" => load_low_curv_assemb(assembly), "pointed" => load_pointed_assemb(assembly), "tridim-icosahedron" => load_tridim_icosahedron_assemb(assembly), + "dodeca-packing" => load_dodeca_packing_assemb(assembly), "balanced" => load_balanced_assemb(assembly), "off-center" => load_off_center_assemb(assembly), "radius-ratio" => load_radius_ratio_assemb(assembly), @@ -829,6 +971,7 @@ pub fn AddRemove() -> View { option(value="low-curv") { "Low-curvature" } option(value="pointed") { "Pointed" } option(value="tridim-icosahedron") { "Tridiminished icosahedron" } + option(value="dodeca-packing") { "Dodecahedral packing" } option(value="balanced") { "Balanced" } option(value="off-center") { "Off-center" } option(value="radius-ratio") { "Radius ratio" }