From 933f05661d7c69595791d61c79e0651ef4c2d2e7 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sun, 10 Nov 2024 16:31:29 -0800 Subject: [PATCH 1/6] Only compile `engine::point` when it's used This function will eventually be used in the application, but right now it's only used in tests. --- app-proto/src/engine.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/app-proto/src/engine.rs b/app-proto/src/engine.rs index 2978a9a..ca05646 100644 --- a/app-proto/src/engine.rs +++ b/app-proto/src/engine.rs @@ -4,6 +4,7 @@ use web_sys::{console, wasm_bindgen::JsValue}; /* DEBUG */ // --- elements --- +#[cfg(test)] pub fn point(x: f64, y: f64, z: f64) -> DVector { DVector::from_column_slice(&[x, y, z, 0.5, 0.5*(x*x + y*y + z*z)]) } From da008fd09075e27e99e2a2d30a8826d26f701434 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sun, 10 Nov 2024 19:24:26 -0800 Subject: [PATCH 2/6] Write out `representation` in `Element` structure --- app-proto/src/add_remove.rs | 28 ++++++++++++++-------------- app-proto/src/assembly.rs | 8 ++++---- app-proto/src/display.rs | 4 +++- app-proto/src/outline.rs | 2 +- 4 files changed, 22 insertions(+), 20 deletions(-) diff --git a/app-proto/src/add_remove.rs b/app-proto/src/add_remove.rs index 92ae4be..6fee142 100644 --- a/app-proto/src/add_remove.rs +++ b/app-proto/src/add_remove.rs @@ -11,7 +11,7 @@ fn load_gen_assemb(assembly: &Assembly) { id: String::from("gemini_a"), label: String::from("Castor"), color: [1.00_f32, 0.25_f32, 0.00_f32], - rep: engine::sphere(0.5, 0.5, 0.0, 1.0), + representation: engine::sphere(0.5, 0.5, 0.0, 1.0), constraints: BTreeSet::default(), index: 0 } @@ -21,7 +21,7 @@ fn load_gen_assemb(assembly: &Assembly) { id: String::from("gemini_b"), label: String::from("Pollux"), color: [0.00_f32, 0.25_f32, 1.00_f32], - rep: engine::sphere(-0.5, -0.5, 0.0, 1.0), + representation: engine::sphere(-0.5, -0.5, 0.0, 1.0), constraints: BTreeSet::default(), index: 0 } @@ -31,7 +31,7 @@ fn load_gen_assemb(assembly: &Assembly) { id: String::from("ursa_major"), label: String::from("Ursa major"), color: [0.25_f32, 0.00_f32, 1.00_f32], - rep: engine::sphere(-0.5, 0.5, 0.0, 0.75), + representation: engine::sphere(-0.5, 0.5, 0.0, 0.75), constraints: BTreeSet::default(), index: 0 } @@ -41,7 +41,7 @@ fn load_gen_assemb(assembly: &Assembly) { id: String::from("ursa_minor"), label: String::from("Ursa minor"), color: [0.25_f32, 1.00_f32, 0.00_f32], - rep: engine::sphere(0.5, -0.5, 0.0, 0.5), + representation: engine::sphere(0.5, -0.5, 0.0, 0.5), constraints: BTreeSet::default(), index: 0 } @@ -51,7 +51,7 @@ fn load_gen_assemb(assembly: &Assembly) { id: String::from("moon_deimos"), label: String::from("Deimos"), color: [0.75_f32, 0.75_f32, 0.00_f32], - rep: engine::sphere(0.0, 0.15, 1.0, 0.25), + representation: engine::sphere(0.0, 0.15, 1.0, 0.25), constraints: BTreeSet::default(), index: 0 } @@ -61,7 +61,7 @@ fn load_gen_assemb(assembly: &Assembly) { id: String::from("moon_phobos"), label: String::from("Phobos"), color: [0.00_f32, 0.75_f32, 0.50_f32], - rep: engine::sphere(0.0, -0.15, -1.0, 0.25), + representation: engine::sphere(0.0, -0.15, -1.0, 0.25), constraints: BTreeSet::default(), index: 0 } @@ -76,7 +76,7 @@ fn load_low_curv_assemb(assembly: &Assembly) { id: "central".to_string(), label: "Central".to_string(), color: [0.75_f32, 0.75_f32, 0.75_f32], - rep: engine::sphere(0.0, 0.0, 0.0, 1.0), + representation: engine::sphere(0.0, 0.0, 0.0, 1.0), constraints: BTreeSet::default(), index: 0 } @@ -86,7 +86,7 @@ fn load_low_curv_assemb(assembly: &Assembly) { id: "assemb_plane".to_string(), label: "Assembly plane".to_string(), color: [0.75_f32, 0.75_f32, 0.75_f32], - rep: engine::sphere_with_offset(0.0, 0.0, 1.0, 0.0, 0.0), + representation: engine::sphere_with_offset(0.0, 0.0, 1.0, 0.0, 0.0), constraints: BTreeSet::default(), index: 0 } @@ -96,7 +96,7 @@ fn load_low_curv_assemb(assembly: &Assembly) { id: "side1".to_string(), label: "Side 1".to_string(), color: [1.00_f32, 0.00_f32, 0.25_f32], - rep: engine::sphere_with_offset(1.0, 0.0, 0.0, 1.0, 0.0), + representation: engine::sphere_with_offset(1.0, 0.0, 0.0, 1.0, 0.0), constraints: BTreeSet::default(), index: 0 } @@ -106,7 +106,7 @@ fn load_low_curv_assemb(assembly: &Assembly) { id: "side2".to_string(), label: "Side 2".to_string(), color: [0.25_f32, 1.00_f32, 0.00_f32], - rep: engine::sphere_with_offset(-0.5, a, 0.0, 1.0, 0.0), + representation: engine::sphere_with_offset(-0.5, a, 0.0, 1.0, 0.0), constraints: BTreeSet::default(), index: 0 } @@ -116,7 +116,7 @@ fn load_low_curv_assemb(assembly: &Assembly) { id: "side3".to_string(), label: "Side 3".to_string(), color: [0.00_f32, 0.25_f32, 1.00_f32], - rep: engine::sphere_with_offset(-0.5, -a, 0.0, 1.0, 0.0), + representation: engine::sphere_with_offset(-0.5, -a, 0.0, 1.0, 0.0), constraints: BTreeSet::default(), index: 0 } @@ -126,7 +126,7 @@ fn load_low_curv_assemb(assembly: &Assembly) { id: "corner1".to_string(), label: "Corner 1".to_string(), color: [0.75_f32, 0.75_f32, 0.75_f32], - rep: engine::sphere(-4.0/3.0, 0.0, 0.0, 1.0/3.0), + representation: engine::sphere(-4.0/3.0, 0.0, 0.0, 1.0/3.0), constraints: BTreeSet::default(), index: 0 } @@ -136,7 +136,7 @@ fn load_low_curv_assemb(assembly: &Assembly) { id: "corner2".to_string(), label: "Corner 2".to_string(), color: [0.75_f32, 0.75_f32, 0.75_f32], - rep: engine::sphere(2.0/3.0, -4.0/3.0 * a, 0.0, 1.0/3.0), + representation: engine::sphere(2.0/3.0, -4.0/3.0 * a, 0.0, 1.0/3.0), constraints: BTreeSet::default(), index: 0 } @@ -146,7 +146,7 @@ fn load_low_curv_assemb(assembly: &Assembly) { id: String::from("corner3"), label: String::from("Corner 3"), color: [0.75_f32, 0.75_f32, 0.75_f32], - rep: engine::sphere(2.0/3.0, 4.0/3.0 * a, 0.0, 1.0/3.0), + representation: engine::sphere(2.0/3.0, 4.0/3.0 * a, 0.0, 1.0/3.0), constraints: BTreeSet::default(), index: 0 } diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index 0970932..27774a1 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -12,7 +12,7 @@ pub struct Element { pub id: String, pub label: String, pub color: [f32; 3], - pub rep: DVector, + pub representation: DVector, pub constraints: BTreeSet, // internal properties, not reflected in any view @@ -86,7 +86,7 @@ impl Assembly { id: id, label: format!("Sphere {}", id_num), color: [0.75_f32, 0.75_f32, 0.75_f32], - rep: DVector::::from_column_slice(&[0.0, 0.0, 0.0, 0.5, -0.5]), + representation: DVector::::from_column_slice(&[0.0, 0.0, 0.0, 0.5, -0.5]), constraints: BTreeSet::default(), index: 0 } @@ -133,7 +133,7 @@ impl Assembly { for (_, elt) in elts { let index = elt.index; gram_to_be.push_sym(index, index, 1.0); - guess_to_be.set_column(index, &elt.rep); + guess_to_be.set_column(index, &elt.representation); } (gram_to_be, guess_to_be) @@ -177,7 +177,7 @@ impl Assembly { // read out the solution self.elements.update(|elts| { for (_, elt) in elts.iter_mut() { - elt.rep.set_column(0, &config.column(elt.index)); + elt.representation.set_column(0, &config.column(elt.index)); } }); } diff --git a/app-proto/src/display.rs b/app-proto/src/display.rs index c32b470..79199ec 100644 --- a/app-proto/src/display.rs +++ b/app-proto/src/display.rs @@ -297,7 +297,9 @@ pub fn Display() -> View { // get the assembly let elements = state.assembly.elements.get_clone(); let element_iter = (&elements).into_iter(); - let reps_world: Vec<_> = element_iter.clone().map(|(_, elt)| &assembly_to_world * &elt.rep).collect(); + let reps_world: Vec<_> = element_iter.clone().map( + |(_, elt)| &assembly_to_world * &elt.representation + ).collect(); let colors: Vec<_> = element_iter.clone().map(|(key, elt)| if state.selection.with(|sel| sel.contains(&key)) { elt.color.map(|ch| 0.2 + 0.8*ch) diff --git a/app-proto/src/outline.rs b/app-proto/src/outline.rs index 8edbe07..9ebf34d 100644 --- a/app-proto/src/outline.rs +++ b/app-proto/src/outline.rs @@ -72,7 +72,7 @@ pub fn Outline() -> View { } }); let label = elt.label.clone(); - let rep_components = elt.rep.iter().map(|u| { + let rep_components = elt.representation.iter().map(|u| { let u_coord = u.to_string().replace("-", "\u{2212}"); View::from(div().children(u_coord)) }).collect::>(); From ed1890bffc9886a79f7c9eea7acb268f660bf5df Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sun, 10 Nov 2024 19:36:40 -0800 Subject: [PATCH 3/6] Improve naming of constraint subjects --- app-proto/src/add_remove.rs | 12 ++++++------ app-proto/src/assembly.rs | 14 +++++++------- app-proto/src/outline.rs | 6 +++--- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/app-proto/src/add_remove.rs b/app-proto/src/add_remove.rs index 6fee142..df91d3c 100644 --- a/app-proto/src/add_remove.rs +++ b/app-proto/src/add_remove.rs @@ -208,16 +208,16 @@ pub fn AddRemove() -> View { }, on:click=|_| { let state = use_context::(); - let args = state.selection.with( + let subjects = state.selection.with( |sel| { - let arg_vec: Vec<_> = sel.into_iter().collect(); - (arg_vec[0].clone(), arg_vec[1].clone()) + let subject_vec: Vec<_> = sel.into_iter().collect(); + (subject_vec[0].clone(), subject_vec[1].clone()) } ); let rep = create_signal(0.0); let active = create_signal(true); state.assembly.insert_constraint(Constraint { - args: args, + subjects: subjects, rep: rep, rep_text: create_signal(String::new()), rep_valid: create_signal(false), @@ -233,8 +233,8 @@ pub fn AddRemove() -> View { for (_, cst) in csts.into_iter() { console::log_5( &JsValue::from(" "), - &JsValue::from(cst.args.0), - &JsValue::from(cst.args.1), + &JsValue::from(cst.subjects.0), + &JsValue::from(cst.subjects.1), &JsValue::from(":"), &JsValue::from(cst.rep.get_untracked()) ); diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index 27774a1..8217922 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -21,7 +21,7 @@ pub struct Element { #[derive(Clone)] pub struct Constraint { - pub args: (usize, usize), + pub subjects: (usize, usize), pub rep: Signal, pub rep_text: Signal, pub rep_valid: Signal, @@ -94,11 +94,11 @@ impl Assembly { } pub fn insert_constraint(&self, constraint: Constraint) { - let args = constraint.args; + let subjects = constraint.subjects; let key = self.constraints.update(|csts| csts.insert(constraint)); self.elements.update(|elts| { - elts[args.0].constraints.insert(key); - elts[args.1].constraints.insert(key); + elts[subjects.0].constraints.insert(key); + elts[subjects.1].constraints.insert(key); }); } @@ -119,9 +119,9 @@ impl Assembly { self.constraints.with_untracked(|csts| { for (_, cst) in csts { if cst.active.get_untracked() && cst.rep_valid.get_untracked() { - let args = cst.args; - let row = elts[args.0].index; - let col = elts[args.1].index; + let subjects = cst.subjects; + let row = elts[subjects.0].index; + let col = elts[subjects.1].index; gram_to_be.push_sym(row, col, cst.rep.get_untracked()); } } diff --git a/app-proto/src/outline.rs b/app-proto/src/outline.rs index 9ebf34d..98b422f 100644 --- a/app-proto/src/outline.rs +++ b/app-proto/src/outline.rs @@ -154,10 +154,10 @@ pub fn Outline() -> View { let c_state = use_context::(); let assembly = &c_state.assembly; let cst = assembly.constraints.with(|csts| csts[c_key].clone()); - let other_arg = if cst.args.0 == key { - cst.args.1 + let other_arg = if cst.subjects.0 == key { + cst.subjects.1 } else { - cst.args.0 + cst.subjects.0 }; let other_arg_label = assembly.elements.with(|elts| elts[other_arg].label.clone()); view! { From ced001bbfe2597bebbda2bcc8659d38d7ab1d3ff Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sun, 10 Nov 2024 20:13:40 -0800 Subject: [PATCH 4/6] Alias the types of element and constraint keys This will make it easier to change the key types if we change how we store and access elements and constraints. --- app-proto/src/assembly.rs | 10 +++++++--- app-proto/src/main.rs | 4 ++-- app-proto/src/outline.rs | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index 8217922..6c4aeb7 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -7,13 +7,17 @@ use web_sys::{console, wasm_bindgen::JsValue}; /* DEBUG */ use crate::engine::{realize_gram, PartialMatrix}; +// the types of the keys we use to access an assembly's elements and constraints +pub type ElementKey = usize; +pub type ConstraintKey = usize; + #[derive(Clone, PartialEq)] pub struct Element { pub id: String, pub label: String, pub color: [f32; 3], pub representation: DVector, - pub constraints: BTreeSet, + pub constraints: BTreeSet, // internal properties, not reflected in any view pub index: usize @@ -21,7 +25,7 @@ pub struct Element { #[derive(Clone)] pub struct Constraint { - pub subjects: (usize, usize), + pub subjects: (ElementKey, ElementKey), pub rep: Signal, pub rep_text: Signal, pub rep_valid: Signal, @@ -36,7 +40,7 @@ pub struct Assembly { pub constraints: Signal>, // indexing - pub elements_by_id: Signal> + pub elements_by_id: Signal> } impl Assembly { diff --git a/app-proto/src/main.rs b/app-proto/src/main.rs index 2c71a83..897f9d4 100644 --- a/app-proto/src/main.rs +++ b/app-proto/src/main.rs @@ -8,14 +8,14 @@ use rustc_hash::FxHashSet; use sycamore::prelude::*; use add_remove::AddRemove; -use assembly::Assembly; +use assembly::{Assembly, ElementKey}; use display::Display; use outline::Outline; #[derive(Clone)] struct AppState { assembly: Assembly, - selection: Signal> + selection: Signal> } impl AppState { diff --git a/app-proto/src/outline.rs b/app-proto/src/outline.rs index 98b422f..0a8816d 100644 --- a/app-proto/src/outline.rs +++ b/app-proto/src/outline.rs @@ -150,7 +150,7 @@ pub fn Outline() -> View { ul(class="constraints") { Keyed( list=elt.constraints.into_iter().collect::>(), - view=move |c_key: usize| { + view=move |c_key| { let c_state = use_context::(); let assembly = &c_state.assembly; let cst = assembly.constraints.with(|csts| csts[c_key].clone()); From b8ca1139d5108a4d7873d4d44060f2daf403d472 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sun, 10 Nov 2024 23:22:30 -0800 Subject: [PATCH 5/6] Explain what the `Element::index` field holds Also, remind us to make the field private when that becomes possible. --- app-proto/src/assembly.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index 6c4aeb7..da54210 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -19,7 +19,11 @@ pub struct Element { pub representation: DVector, pub constraints: BTreeSet, - // internal properties, not reflected in any view + // the configuration matrix column index that was assigned to this element + // last time the assembly was realized + /* TO DO */ + // this is public, as a kludge, because `Element` doesn't have a constructor + // yet. it should be made private as soon as the constructor is written pub index: usize } From a170492e3dc1a3556062f6174551195787ce014c Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sun, 10 Nov 2024 23:47:34 -0800 Subject: [PATCH 6/6] Add engine conventions to inversive coordinates notes --- notes/inversive.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/notes/inversive.md b/notes/inversive.md index 5ee6329..4ca8091 100644 --- a/notes/inversive.md +++ b/notes/inversive.md @@ -41,3 +41,23 @@ I will have to work out formulas for the Euclidean distance between two entities In this vein, it seems as though if J1 and J2 are the reps of two points, then Q(J1,J2) = d^2/2. So then the sphere centered at J1 through J2 is (J1-(2Q(J1,J2),0,0,0,0))/sqrt(2Q(J1,J2)). Ugh has a sqrt in it. Similarly for sphere centered at J3 through J2, (J3-(2Q(J3,J2),0000))/sqrt(2Q(J3,J2)). J1,J2,J3 are collinear if these spheres are tangent, i.e. if those vectors have Q-inner-product 1, which is to say Q(J1,J3) - Q(J1,J2) - Q(J3,J2) = 2sqrt(Q(J1,J2)Q(J2,J3)). But maybe that's not the simplest way of putting it. After all, we can just say that the cross-product of the two differences is 0; that has no square roots in it. One conceivable way to canonicalize lines is to use the *perpendicular* plane that goes through the origin, that's uniquely defined, and anyway just amounts to I = (0,0,d) where d is the ordinary direction vector of the line; and a point J in that plane that the line goes through, which just amounts to J=(r^2,1,E) with Q(I,J) = 0, i.e. E\dot d = 0. It's also the point on the line closest to the origin. The reason that we don't usually use that point as the companion to the direction vector is that the resulting set of six coordinates is not homogeneous. But here that's not an issue, since we have our standard point coordinates and plane coordinates; and for a plane through the origin, only two of the direction coordinates are really free, and then we have the one dot-product relation, so only two of the point coordinates are really free, giving us the correct dimensionality of 4 for the set of lines. So in some sense this says that we could take naively as coordinates for a line the projection of the unit direction vector to the xy plane and the projection of the line's closest point to the origin to the xy plane. That doesn't seem to have any weird gimbal locks or discontinuities or anything. And with these coordinates, you can test if the point E=x,y,z is on the line (dx,dy,cx,cy) by extending (dx,dy) to d via dz = sqrt(1-dx^2 - dy^2), extending (cx,cy) to c by determining cz via d\dot c = 0, and then checking if d\cross(E-c) = 0. And you can see if two lines are parallel just by checking if they have the same direction vector, and if not, you can see if they are coplanar by projecting both of their closest points perpendicularly onto the line in the direction of the cross product of their directions, and if the projections match they are coplanar. + +#### Engine Conventions + +The coordinate conventions used in the engine are different from the ones used in these notes. Marking the engine vectors and coordinates with $'$, we have +$$I' = (x', y', z', b', c'),$$ +where +$$\begin{align*} +x' & = x & b' & = b/2 \\ +y' & = y & c' & = c/2. \\ +z' & = z +\end{align*}$$ +The engine uses the quadratic form $Q' = -Q$, which is expressed in engine coordinates as +$$Q'(I'_1, I'_2) = x'_1 x'_2 + y'_1 y'_2 + z'_1 z'_2 - 2(b'_1c'_2 + c'_1 b'_2).$$ +In the `engine` module, the matrix of $Q'$ is encoded in the lazy static variable `Q`. + +In the engine's coordinate conventions, a sphere with radius $r > 0$ centered on $P = (P_x, P_y, P_z)$ is represented by the vector +$$I'_s = \left(\frac{P_x}{r}, \frac{P_y}{r}, \frac{P_z}{r}, \frac1{2r}, \frac{\|P\|^2 - r^2}{2r}\right),$$ +which has the normalization $Q'(I'_s, I'_s) = 1$. The point $P$ is represented by the vector +$$I'_P = \left(P_x, P_y, P_z, \frac{1}{2}, \frac{\|P\|^2}{2}\right).$$ +In the `engine` module, these formulas are encoded in the `sphere` and `point` functions. \ No newline at end of file