diff --git a/app-proto/rust-toolchain.toml b/app-proto/rust-toolchain.toml new file mode 100644 index 0000000..5d56faf --- /dev/null +++ b/app-proto/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index c263692..acef6c8 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -274,14 +274,14 @@ impl Point { const NORM_COMPONENT: usize = 4; pub fn new( - id: String, - label: String, + id: &str, + label: &str, color: ElementColor, representation: DVector, ) -> Self { Self { - id, - label, + id: id.to_string(), + label: label.to_string(), color, representation: create_signal(representation), ghost: create_signal(false), @@ -299,8 +299,8 @@ impl Element for Point { fn default(id: String, id_num: u64) -> Self { Self::new( - id, - format!("Point {id_num}"), + &id, + &format!("Point {id_num}"), [0.75_f32, 0.75_f32, 0.75_f32], point(0.0, 0.0, 0.0), ) diff --git a/app-proto/src/components/test_assembly_chooser.rs b/app-proto/src/components/test_assembly_chooser.rs index 0d387d3..86ad836 100644 --- a/app-proto/src/components/test_assembly_chooser.rs +++ b/app-proto/src/components/test_assembly_chooser.rs @@ -15,10 +15,61 @@ use crate::{ Sphere, }, engine, - engine::DescentHistory, + engine::{DescentHistory, point}, specified::SpecifiedValue, }; +// Convenience: use sqrt() as a function to get the square root of +// anything that can be losslessly converted to f64, since we happen +// to always want our sqrts to be f64. +// RUST KVETCHES: (I) Why is this so convoluted? +// In particular, what would be so bad about allowing +// const fn sqrt>(x: T) -> f64 { (x as f64).sqrt() } +// ??? +// (II) Oh dear, sqrt is not computable in a const context, so we have to +// roll our own. I hope I get it right! I definitely don't know how to ensure +// that the final double is correctly rounded :-( + +const SIXTH: f64 = 1./6.; +const FOURTH: f64 = 1./4.; +const fn invsqrt(xin: f64) -> f64 { + if !xin.is_finite() { return f64::NAN; } + let mut log4 = 0; + let mut x = xin; + while x < 1. { x *= 4.; log4 -= 1; } + while x > 4. { x *= FOURTH; log4 += 1; } + let mut next = 1.1 - SIXTH * x; + let mut diff = 1.; + while diff > 4. * f64::EPSILON { // termination condition?? + let last = next; + next = last * (1.5 - 0.5 * x * last * last); + diff = next - last; + if diff < 0. { diff *= -1.; } + } + while log4 > 0 { next *= 0.5; log4 -= 1; } + while log4 < 0 { next *= 2.; log4 += 1; } + return next; +} + +#[const_trait] +trait ConstSqrt {fn sqr(self) -> f64;} +impl const ConstSqrt for f64 {fn sqr(self) -> f64 {self * invsqrt(self)}} +impl const ConstSqrt for i32 {fn sqr(self) -> f64 {(self as f64).sqr()}} +const fn sqrt(x: T) -> f64 {x.sqr()} + +// RUST KVETCHES: (I) It is annoying that we must redundantly specify +// the type of the well-typed RHS to assign it to a const. +// See https://github.com/rust-lang/rfcs/pull/3546 +// Can we fix this in husht? Seems like we will need to be able to do full +// rust type inference in the transpiler; how will we accomplish that? +// (2) It is very annoying that there doesn't seem to be any way to specify +// that we want an expression like `5.0 / 2` to be evaluated by converting +// the 2 to a float, because of the "orphan rule". Can we fix this in husht? +// Again, it seems we would need full rust type inference. + +// FIXME: replace with std::f64::consts::PHI when that gets stabilized +const PHI: f64 = (1. + sqrt(5)) / 2.; + // --- loaders --- /* DEBUG */ @@ -79,7 +130,7 @@ fn load_general(assembly: &Assembly) { fn load_low_curvature(assembly: &Assembly) { // create the spheres - let a = 0.75_f64.sqrt(); + const A: f64 = sqrt(0.75); let _ = assembly.try_insert_element( Sphere::new( "central".to_string(), @@ -109,7 +160,7 @@ fn load_low_curvature(assembly: &Assembly) { "side2".to_string(), "Side 2".to_string(), [0.25_f32, 1.00_f32, 0.00_f32], - engine::sphere_with_offset(-0.5, a, 0.0, 1.0, 0.0), + engine::sphere_with_offset(-0.5, A, 0.0, 1.0, 0.0), ) ); let _ = assembly.try_insert_element( @@ -117,7 +168,7 @@ fn load_low_curvature(assembly: &Assembly) { "side3".to_string(), "Side 3".to_string(), [0.00_f32, 0.25_f32, 1.00_f32], - engine::sphere_with_offset(-0.5, -a, 0.0, 1.0, 0.0), + engine::sphere_with_offset(-0.5, -A, 0.0, 1.0, 0.0), ) ); let _ = assembly.try_insert_element( @@ -133,7 +184,7 @@ fn load_low_curvature(assembly: &Assembly) { "corner2".to_string(), "Corner 2".to_string(), [0.75_f32, 0.75_f32, 0.75_f32], - engine::sphere(2.0/3.0, -4.0/3.0 * a, 0.0, 1.0/3.0), + engine::sphere(2.0/3.0, -4.0/3.0 * A, 0.0, 1.0/3.0), ) ); let _ = assembly.try_insert_element( @@ -141,7 +192,7 @@ fn load_low_curvature(assembly: &Assembly) { String::from("corner3"), String::from("Corner 3"), [0.75_f32, 0.75_f32, 0.75_f32], - engine::sphere(2.0/3.0, 4.0/3.0 * a, 0.0, 1.0/3.0), + engine::sphere(2.0/3.0, 4.0/3.0 * A, 0.0, 1.0/3.0), ) ); @@ -199,16 +250,16 @@ fn load_low_curvature(assembly: &Assembly) { fn load_pointed(assembly: &Assembly) { let _ = assembly.try_insert_element( Point::new( - format!("point_front"), - format!("Front point"), + "point_front", + "Front point", [0.75_f32, 0.75_f32, 0.75_f32], engine::point(0.0, 0.0, FRAC_1_SQRT_2), ) ); let _ = assembly.try_insert_element( Point::new( - format!("point_back"), - format!("Back point"), + "point_back", + "Back point", [0.75_f32, 0.75_f32, 0.75_f32], engine::point(0.0, 0.0, -FRAC_1_SQRT_2), ) @@ -229,8 +280,8 @@ fn load_pointed(assembly: &Assembly) { let _ = assembly.try_insert_element( Point::new( - format!("point{index_x}{index_y}"), - format!("Point {index_x}{index_y}"), + &format!("point{index_x}{index_y}"), + &format!("Point {index_x}{index_y}"), [0.5*(1.0 + x) as f32, 0.5*(1.0 + y) as f32, 0.5*(1.0 - x*y) as f32], engine::point(x, y, 0.0), ) @@ -252,60 +303,15 @@ fn load_tridiminished_icosahedron(assembly: &Assembly) { const COLOR_B: ElementColor = [0.75_f32, 0.75_f32, 0.75_f32]; const COLOR_C: ElementColor = [0.25_f32, 0.50_f32, 1.00_f32]; let vertices = [ - Point::new( - "a1".to_string(), - "A₁".to_string(), - COLOR_A, - engine::point(0.25, 0.75, 0.75), - ), - Point::new( - "a2".to_string(), - "A₂".to_string(), - COLOR_A, - engine::point(0.75, 0.25, 0.75), - ), - Point::new( - "a3".to_string(), - "A₃".to_string(), - COLOR_A, - engine::point(0.75, 0.75, 0.25), - ), - Point::new( - "b1".to_string(), - "B₁".to_string(), - COLOR_B, - engine::point(0.75, -0.25, -0.25), - ), - Point::new( - "b2".to_string(), - "B₂".to_string(), - COLOR_B, - engine::point(-0.25, 0.75, -0.25), - ), - Point::new( - "b3".to_string(), - "B₃".to_string(), - COLOR_B, - engine::point(-0.25, -0.25, 0.75), - ), - Point::new( - "c1".to_string(), - "C₁".to_string(), - COLOR_C, - engine::point(0.0, -1.0, -1.0), - ), - Point::new( - "c2".to_string(), - "C₂".to_string(), - COLOR_C, - engine::point(-1.0, 0.0, -1.0), - ), - Point::new( - "c3".to_string(), - "C₃".to_string(), - COLOR_C, - engine::point(-1.0, -1.0, 0.0), - ), + Point::new("a1", "A₁", COLOR_A, point( 0.25, 0.75, 0.75)), + Point::new("a2", "A₂", COLOR_A, point( 0.75, 0.25, 0.75)), + Point::new("a3", "A₃", COLOR_A, point( 0.75, 0.75, 0.25)), + Point::new("b1", "B₁", COLOR_B, point( 0.75, -0.25, -0.25)), + Point::new("b2", "B₂", COLOR_B, point(-0.25, 0.75, -0.25)), + Point::new("b3", "B₃", COLOR_B, point(-0.25, -0.25, 0.75)), + Point::new("c1", "C₁", COLOR_C, point( 0.0, -1.0, -1.0)), + Point::new("c2", "C₂", COLOR_C, point(-1.0, 0.0, -1.0)), + Point::new("c3", "C₃", COLOR_C, point(-1.0, -1.0, 0.0)), ]; for vertex in vertices { let _ = assembly.try_insert_element(vertex); @@ -313,26 +319,26 @@ fn load_tridiminished_icosahedron(assembly: &Assembly) { // create the faces const COLOR_FACE: ElementColor = [0.75_f32, 0.75_f32, 0.75_f32]; - let frac_1_sqrt_6 = 1.0 / 6.0_f64.sqrt(); - let frac_2_sqrt_6 = 2.0 * frac_1_sqrt_6; + const SQRT_1_6: f64 = invsqrt(6.); + const SQRT_2_3: f64 = 2. * SQRT_1_6; let faces = [ Sphere::new( "face1".to_string(), "Face 1".to_string(), COLOR_FACE, - engine::sphere_with_offset(frac_2_sqrt_6, -frac_1_sqrt_6, -frac_1_sqrt_6, -frac_1_sqrt_6, 0.0), + engine::sphere_with_offset(SQRT_2_3, -SQRT_1_6, -SQRT_1_6, -SQRT_1_6, 0.0), ), Sphere::new( "face2".to_string(), "Face 2".to_string(), COLOR_FACE, - engine::sphere_with_offset(-frac_1_sqrt_6, frac_2_sqrt_6, -frac_1_sqrt_6, -frac_1_sqrt_6, 0.0), + engine::sphere_with_offset(-SQRT_1_6, SQRT_2_3, -SQRT_1_6, -SQRT_1_6, 0.0), ), Sphere::new( "face3".to_string(), "Face 3".to_string(), COLOR_FACE, - engine::sphere_with_offset(-frac_1_sqrt_6, -frac_1_sqrt_6, frac_2_sqrt_6, -frac_1_sqrt_6, 0.0), + engine::sphere_with_offset(-SQRT_1_6, -SQRT_1_6, SQRT_2_3, -SQRT_1_6, 0.0), ), ]; for face in faces { @@ -434,17 +440,16 @@ fn load_dodecahedral_packing(assembly: &Assembly) { 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]; + const PHI_INV: f64 = 1.0 / PHI; + const COORD_SCALE: f64 = sqrt(PHI + 2.0); + const FACE_SCALES: [f64; 2] = [PHI_INV, (13.0 / 12.0) / COORD_SCALE]; + const FACE_RADII: [f64; 2] = [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 small_coord = FACE_SCALES[k] * if j > 0 {1.} else {-1.}; + let big_coord = FACE_SCALES[k] * PHI * if k > 0 {1.} else {-1.}; let id_num = format!("{j}{k}"); let label_sub = format!("{}{}", subscripts[j], subscripts[k]); @@ -456,7 +461,7 @@ fn load_dodecahedral_packing(assembly: &Assembly) { id_a.clone(), format!("A{label_sub}"), COLOR_A, - engine::sphere(0.0, small_coord, big_coord, face_radii[k]), + engine::sphere(0.0, small_coord, big_coord, FACE_RADII[k]), ) ); faces.push( @@ -472,7 +477,7 @@ fn load_dodecahedral_packing(assembly: &Assembly) { id_b.clone(), format!("B{label_sub}"), COLOR_B, - engine::sphere(small_coord, big_coord, 0.0, face_radii[k]), + engine::sphere(small_coord, big_coord, 0.0, FACE_RADII[k]), ) ); faces.push( @@ -488,7 +493,7 @@ fn load_dodecahedral_packing(assembly: &Assembly) { id_c.clone(), format!("C{label_sub}"), COLOR_C, - engine::sphere(big_coord, 0.0, small_coord, face_radii[k]), + engine::sphere(big_coord, 0.0, small_coord, FACE_RADII[k]), ) ); faces.push( @@ -614,12 +619,7 @@ fn load_balanced(assembly: &Assembly) { fn load_off_center(assembly: &Assembly) { // create a point almost at the origin and a sphere centered on the origin let _ = assembly.try_insert_element( - Point::new( - "point".to_string(), - "Point".to_string(), - [0.75_f32, 0.75_f32, 0.75_f32], - engine::point(1e-9, 0.0, 0.0), - ), + Point::new("point", "Point", [0.75, 0.75, 0.75], point(1e-9, 0.0, 0.0)), ); let _ = assembly.try_insert_element( Sphere::new( @@ -689,8 +689,8 @@ fn load_radius_ratio(assembly: &Assembly) { ).map( |(k, color, representation)| { Point::new( - format!("v{k}"), - format!("Vertex {k}"), + &format!("v{k}"), + &format!("Vertex {k}"), color, representation, ) @@ -882,6 +882,76 @@ fn load_irisawa_hexlet(assembly: &Assembly) { assembly.insert_regulator(Rc::new(outer_moon_tangency)); } +const HPHI: f64 = PHI / 2.; // "half phi" +const RTHPHI: f64 = sqrt(HPHI); // "root half phi" +const RTPHIPH: f64 = sqrt(PHI + 0.5); // "root phi plus (half)" +const P: bool = true; // Pinned +const F: bool = false; // Free + +// Initial data for the vertices commmon to 554aug2 and 554domed. +// Used the Vectornaut near_miss branch final positions for 554aug2, +// to the 3 decimal places currently shown. +const ACRON554_COMMON: [(&str, (f64, f64, f64), bool, usize, &str); 38] = [ + // id, coordinates, Pin/Free, level, earlier neighbor IDs. + ("A_NE", ( 0.5, 0.5, 0.), P, 0, ""), + ("A_NW", (-0.5, 0.5, 0.), P, 0, ""), + ("A_SE", ( 0.5, -0.5, 0.), P, 0, ""), + ("A_SW", (-0.5, -0.5, 0.), P, 0, ""), + ("Z_E", ( 0.229, -0.002, 0.821), F, 1, "A_NE,A_SE"), + ("Z_S", ( 0.002, -0.229, 0.821), F, 1, "A_SE,A_SW"), + ("B_NE", ( HPHI, HPHI, RTHPHI), P, 2, "Z_E"), + ("B_NW", (-HPHI, HPHI, RTHPHI), P, 2, ""), + ("B_SW", (-HPHI, -HPHI, RTHPHI), P, 2, "Z_S"), + ("B_SE", ( 0.812, -0.812, 0.89), F, 2, "Z_E,Z_S"), + ("Y_NE", ( 0.11, 0.103, 1.019), F, 3, "B_NE"), + ("Y_NW", (-0.103, 0.103, 1.02), F, 3, "B_NW"), + ("Y_SE", ( 0.11, -0.11, 1.017), F, 3, "B_SE"), + ("Y_SW", (-0.103, -0.11, 1.019), F, 3, "B_SW"), + ("C_N", ( 0., 1., RTPHIPH), P, 4, "Y_NE,Y_NW"), + ("C_W", (-1., 0., RTPHIPH), P, 4, "Y_NW,Y_SW"), + ("C_E", ( 1.006, -0.006, 1.45), F, 4, "Y_NE,Y_SE"), + ("C_S", ( 0.006, -1.006, 1.45), F, 4, "Y_SE,Y_SW"), + ("D_NE", ( 0.2, 0.181, 2.011), F, 5, "Y_NE,C_N,C_E"), + ("D_NW", (-0.181, 0.181, 2.014), F, 5, "Y_NW,C_N,C_W"), + ("D_SE", ( 0.2, -0.2, 2.009), F, 5, "Y_SE,C_E,C_S"), + ("D_SW", (-0.181, -0.2, 2.011), F, 5, "Y_SW,C_W,C_S"), + ("E_N", ( 0.012, 1.055, 2.46), F, 6, "C_N,D_NE,D_NW"), + ("E_W", (-1.055, -0.012, 2.46), F, 6, "C_W,D_NW,D_SW"), + ("E_E", ( 1.079, -0.012, 2.447), F, 6, "C_E,D_NE,D_SE"), + ("E_S", ( 0.012, -1.079, 2.447), F, 6, "C_S,D_SE,D_SW"), + ("F_NE", ( 0.296, 0.265, 3.003), F, 7, "D_NE,E_N,E_E"), + ("F_NW", (-0.265, 0.265, 3.007), F, 7, "D_NW,E_N,E_W"), + ("F_SE", ( 0.296, -0.296, 3.0), F, 7, "D_SE,E_E,E_S"), + ("F_SW", (-0.265, -0.296, 3.003), F, 7, "D_SW,E_W,E_S"), + ("G_1", ( 0.517, 1.19, 3.312), F, 8, "E_N,F_NE"), + ("G_2", ( 1.224, 0.483, 3.304), F, 8, "E_E,F_NE,G_1"), + ("G_4", ( 1.224, -0.517, 3.298), F, 8, "E_E,F_SE,G_2"), + ("G_5", ( 0.517, -1.224, 3.298), F, 8, "E_S,F_SE,G_4"), + ("G_7", (-0.483, -1.224, 3.304), F, 8, "E_S,F_SW,G_5"), + ("G_8", (-1.19, -0.517, 3.312), F, 8, "E_W,F_SW,G_7"), + ("G_10", (-1.19, 0.483, 3.318), F, 8, "E_W,F_NW,G_8"), + ("G_11", (-0.483, 1.19, 3.318), F, 8, "E_N,F_NW,G_1,G_10"), +]; + +const LEVEL_COLORS: [[f32; 3]; 9] = [ + [0.75, 0., 0.75], + [1., 0.4, 0.6], + [1., 0., 0.25], + [1., 0.75, 0.25], + [0.75, 0.5, 0.], + [0.9, 0.9, 0.], + [0.25, 0.75, 0.], + [0., 0.5, 0.75], + [0.25, 0., 1.], +]; + +fn load_554aug2(assembly: &Assembly) { + for (id, v, _pinned, level, _neighbors) in ACRON554_COMMON { + let pt = Point::new(id, id, LEVEL_COLORS[level], point(v.0, v.1, v.2)); + assembly.try_insert_element(pt); + } +} + // --- chooser --- /* DEBUG */ @@ -918,11 +988,14 @@ pub fn TestAssemblyChooser() -> View { "off-center" => load_off_center(assembly), "radius-ratio" => load_radius_ratio(assembly), "irisawa-hexlet" => load_irisawa_hexlet(assembly), + "aug554" => load_554aug2(assembly), _ => (), }; }); }); - + + // FIXME: Non-DRY -- should not need to reiterate thie list of assembly + // labels // build the chooser view! { select(bind:value = assembly_name) { @@ -935,6 +1008,7 @@ pub fn TestAssemblyChooser() -> View { option(value = "off-center") { "Off-center" } option(value = "radius-ratio") { "Radius ratio" } option(value = "irisawa-hexlet") { "Irisawa hexlet" } + option(value = "aug554") { "McNeill acron 554" } option(value = "empty") { "Empty" } } } diff --git a/app-proto/src/lib.rs b/app-proto/src/lib.rs index 0d9bc4a..f37e0d6 100644 --- a/app-proto/src/lib.rs +++ b/app-proto/src/lib.rs @@ -1 +1,3 @@ +#![feature(const_trait_impl)] + pub mod engine; \ No newline at end of file diff --git a/app-proto/src/main.rs b/app-proto/src/main.rs index a03b026..cb51571 100644 --- a/app-proto/src/main.rs +++ b/app-proto/src/main.rs @@ -1,3 +1,5 @@ +#![feature(const_trait_impl)] + mod assembly; mod components; mod engine;