From 9b39fe56b810d576f41fc43237f7cd85590e6f6d Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Thu, 26 Sep 2024 19:10:34 -0700 Subject: [PATCH 01/14] Outline: include constraints in element diff key This tells Sycamore that the outline view of an element should update when the element's constraint set has changed. To make the constraint set hashable, so we can include it in the diff key, we store it as a `BTreeSet` instead of an `FxHashSet`. --- app-proto/full-interface/src/assembly.rs | 4 ++-- app-proto/full-interface/src/main.rs | 7 ++++--- app-proto/full-interface/src/outline.rs | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/app-proto/full-interface/src/assembly.rs b/app-proto/full-interface/src/assembly.rs index 6fac59f..1d0a6f8 100644 --- a/app-proto/full-interface/src/assembly.rs +++ b/app-proto/full-interface/src/assembly.rs @@ -1,6 +1,6 @@ use nalgebra::DVector; -use rustc_hash::FxHashSet; use slab::Slab; +use std::collections::BTreeSet; use sycamore::prelude::*; #[derive(Clone, PartialEq)] @@ -9,7 +9,7 @@ pub struct Element { pub label: String, pub color: [f32; 3], pub rep: DVector, - pub constraints: FxHashSet + pub constraints: BTreeSet } #[derive(Clone)] diff --git a/app-proto/full-interface/src/main.rs b/app-proto/full-interface/src/main.rs index 2f31ada..e867ad3 100644 --- a/app-proto/full-interface/src/main.rs +++ b/app-proto/full-interface/src/main.rs @@ -5,6 +5,7 @@ mod outline; use nalgebra::DVector; use rustc_hash::FxHashSet; +use std::collections::BTreeSet; use sycamore::prelude::*; use add_remove::AddRemove; @@ -37,7 +38,7 @@ fn main() { label: String::from("Wing A"), color: [1.00_f32, 0.25_f32, 0.00_f32], rep: DVector::::from_column_slice(&[0.5, 0.5, 0.0, 0.5, -0.25]), - constraints: FxHashSet::default() + constraints: BTreeSet::default() } ) ); @@ -48,7 +49,7 @@ fn main() { label: String::from("Wing B"), color: [0.00_f32, 0.25_f32, 1.00_f32], rep: DVector::::from_column_slice(&[-0.5, -0.5, 0.0, 0.5, -0.25]), - constraints: FxHashSet::default() + constraints: BTreeSet::default() }, ) ); @@ -59,7 +60,7 @@ fn main() { label: String::from("Central"), color: [0.75_f32, 0.75_f32, 0.75_f32], rep: DVector::::from_column_slice(&[0.0, 0.0, 0.0, 0.4, -0.625]), - constraints: FxHashSet::default() + constraints: BTreeSet::default() } ) ); diff --git a/app-proto/full-interface/src/outline.rs b/app-proto/full-interface/src/outline.rs index be5a9b1..8a0a3fb 100644 --- a/app-proto/full-interface/src/outline.rs +++ b/app-proto/full-interface/src/outline.rs @@ -148,7 +148,7 @@ pub fn Outline() -> View { } } }, - key=|(key, _)| key.clone() + key=|(key, elt)| (key.clone(), elt.constraints.clone()) ) } } From b3afd6f5553f6f1ca39d80ab38c84f9af43a1261 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Thu, 26 Sep 2024 19:16:41 -0700 Subject: [PATCH 02/14] App: Store selection in `BTreeSet` Since we're using `BTreeSet` for element constraint sets now, we might as well use it for the selection set too. This removes the `rustc-hash` dependency. --- app-proto/full-interface/Cargo.toml | 1 - app-proto/full-interface/src/main.rs | 5 ++--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/app-proto/full-interface/Cargo.toml b/app-proto/full-interface/Cargo.toml index 920469a..7640b07 100644 --- a/app-proto/full-interface/Cargo.toml +++ b/app-proto/full-interface/Cargo.toml @@ -11,7 +11,6 @@ default = ["console_error_panic_hook"] itertools = "0.13.0" js-sys = "0.3.70" nalgebra = "0.33.0" -rustc-hash = "2.0.0" slab = "0.4.9" sycamore = "0.9.0-beta.3" diff --git a/app-proto/full-interface/src/main.rs b/app-proto/full-interface/src/main.rs index e867ad3..87e06db 100644 --- a/app-proto/full-interface/src/main.rs +++ b/app-proto/full-interface/src/main.rs @@ -4,7 +4,6 @@ mod display; mod outline; use nalgebra::DVector; -use rustc_hash::FxHashSet; use std::collections::BTreeSet; use sycamore::prelude::*; @@ -16,14 +15,14 @@ use outline::Outline; #[derive(Clone)] struct AppState { assembly: Assembly, - selection: Signal> + selection: Signal> } impl AppState { fn new() -> AppState { AppState { assembly: Assembly::new(), - selection: create_signal(FxHashSet::default()) + selection: create_signal(BTreeSet::default()) } } } From 2444649dd1d138b1d31b56ad026ff879e86586c6 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Thu, 26 Sep 2024 19:17:57 -0700 Subject: [PATCH 03/14] AddRemove: underscore unused event variables --- app-proto/full-interface/src/add_remove.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app-proto/full-interface/src/add_remove.rs b/app-proto/full-interface/src/add_remove.rs index 59220ae..e0ecf0f 100644 --- a/app-proto/full-interface/src/add_remove.rs +++ b/app-proto/full-interface/src/add_remove.rs @@ -1,5 +1,5 @@ use sycamore::prelude::*; -use web_sys::{MouseEvent, console, wasm_bindgen::JsValue}; +use web_sys::{console, wasm_bindgen::JsValue}; use crate::AppState; use crate::Constraint; @@ -11,7 +11,7 @@ pub fn AddRemove() -> View { view! { div(id="add-remove") { button( - on:click=move |event: MouseEvent| { + on:click=move |_| { console::log_1(&JsValue::from("constraints:")); state.assembly.constraints.with(|csts| { for (_, cst) in csts.into_iter() { @@ -29,7 +29,7 @@ pub fn AddRemove() -> View { disabled={ state.selection.with(|sel| sel.len() != 2) }, - on:click=move |event: MouseEvent| { + on:click=move |_| { let args = state.selection.with( |sel| { let arg_vec: Vec<_> = sel.into_iter().collect(); From bd0982f821a0f8d4cd5d1128f4e1c72e88876831 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Fri, 27 Sep 2024 14:33:49 -0700 Subject: [PATCH 04/14] AddRemove: make a button that adds elements In the process, switch selection storage back to `FxHashSet`, reverting commit b3afd6f. --- app-proto/full-interface/Cargo.toml | 1 + app-proto/full-interface/src/add_remove.rs | 68 +++++++++++++--------- app-proto/full-interface/src/assembly.rs | 33 ++++++++++- app-proto/full-interface/src/main.rs | 5 +- 4 files changed, 74 insertions(+), 33 deletions(-) diff --git a/app-proto/full-interface/Cargo.toml b/app-proto/full-interface/Cargo.toml index 7640b07..920469a 100644 --- a/app-proto/full-interface/Cargo.toml +++ b/app-proto/full-interface/Cargo.toml @@ -11,6 +11,7 @@ default = ["console_error_panic_hook"] itertools = "0.13.0" js-sys = "0.3.70" nalgebra = "0.33.0" +rustc-hash = "2.0.0" slab = "0.4.9" sycamore = "0.9.0-beta.3" diff --git a/app-proto/full-interface/src/add_remove.rs b/app-proto/full-interface/src/add_remove.rs index e0ecf0f..f93f31f 100644 --- a/app-proto/full-interface/src/add_remove.rs +++ b/app-proto/full-interface/src/add_remove.rs @@ -6,16 +6,51 @@ use crate::Constraint; #[component] pub fn AddRemove() -> View { - let state = use_context::(); - view! { div(id="add-remove") { button( - on:click=move |_| { + on:click=|_| { + let state = use_context::(); + state.assembly.insert_new_element(); + + /* DEBUG */ + // print updated list of elements by identifier + console::log_1(&JsValue::from("elements by identifier:")); + for (id, key) in state.assembly.elements_by_id.get_clone().iter() { + console::log_3( + &JsValue::from(" "), + &JsValue::from(id), + &JsValue::from(*key) + ); + } + } + ) { "+" } + button( + disabled={ + let state = use_context::(); + state.selection.with(|sel| sel.len() != 2) + }, + on:click=|_| { + let state = use_context::(); + let args = state.selection.with( + |sel| { + let arg_vec: Vec<_> = sel.into_iter().collect(); + (arg_vec[0].clone(), arg_vec[1].clone()) + } + ); + state.assembly.insert_constraint(Constraint { + args: args, + rep: 0.0 + }); + state.selection.update(|sel| sel.clear()); + + /* DEBUG */ + // print updated constraint list console::log_1(&JsValue::from("constraints:")); state.assembly.constraints.with(|csts| { for (_, cst) in csts.into_iter() { - console::log_4( + console::log_5( + &JsValue::from(" "), &JsValue::from(cst.args.0), &JsValue::from(cst.args.1), &JsValue::from(":"), @@ -24,31 +59,6 @@ pub fn AddRemove() -> View { } }); } - ) { "+" } - button( - disabled={ - state.selection.with(|sel| sel.len() != 2) - }, - on:click=move |_| { - let args = state.selection.with( - |sel| { - let arg_vec: Vec<_> = sel.into_iter().collect(); - (arg_vec[0].clone(), arg_vec[1].clone()) - } - ); - console::log_5( - &JsValue::from("add constraint"), - &JsValue::from(args.0), - &JsValue::from(args.1), - &JsValue::from(":"), - &JsValue::from(0.0) - ); - state.assembly.insert_constraint(Constraint { - args: args, - rep: 0.0 - }); - state.selection.update(|sel| sel.clear()); - } ) { "🔗" } } } diff --git a/app-proto/full-interface/src/assembly.rs b/app-proto/full-interface/src/assembly.rs index 1d0a6f8..aa4355e 100644 --- a/app-proto/full-interface/src/assembly.rs +++ b/app-proto/full-interface/src/assembly.rs @@ -1,4 +1,5 @@ use nalgebra::DVector; +use rustc_hash::FxHashMap; use slab::Slab; use std::collections::BTreeSet; use sycamore::prelude::*; @@ -21,18 +22,46 @@ pub struct Constraint { // a complete, view-independent description of an assembly #[derive(Clone)] pub struct Assembly { + // elements and constraints pub elements: Signal>, - pub constraints: Signal> + pub constraints: Signal>, + + // indexing + pub elements_by_id: Signal> } impl Assembly { pub fn new() -> Assembly { Assembly { elements: create_signal(Slab::new()), - constraints: create_signal(Slab::new()) + constraints: create_signal(Slab::new()), + elements_by_id: create_signal(FxHashMap::default()) } } + pub fn insert_new_element(&self) { + // find the next unused identifier in the default sequence + let mut id_num = 1; + let mut id = format!("sphere{}", id_num); + while self.elements_by_id.with( + |elts_by_id| elts_by_id.contains_key(&id) + ) { + id_num += 1; + id = format!("sphere{}", id_num); + } + + // create and insert a new element + let elt = Element { + id: id.clone(), + 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]), + constraints: BTreeSet::default() + }; + let key = self.elements.update(|elts| elts.insert(elt)); + self.elements_by_id.update(|elts_by_id| elts_by_id.insert(id, key)); + } + pub fn insert_constraint(&self, constraint: Constraint) { let args = constraint.args; let key = self.constraints.update(|csts| csts.insert(constraint)); diff --git a/app-proto/full-interface/src/main.rs b/app-proto/full-interface/src/main.rs index 87e06db..e867ad3 100644 --- a/app-proto/full-interface/src/main.rs +++ b/app-proto/full-interface/src/main.rs @@ -4,6 +4,7 @@ mod display; mod outline; use nalgebra::DVector; +use rustc_hash::FxHashSet; use std::collections::BTreeSet; use sycamore::prelude::*; @@ -15,14 +16,14 @@ use outline::Outline; #[derive(Clone)] struct AppState { assembly: Assembly, - selection: Signal> + selection: Signal> } impl AppState { fn new() -> AppState { AppState { assembly: Assembly::new(), - selection: create_signal(BTreeSet::default()) + selection: create_signal(FxHashSet::default()) } } } From b08dbd6f936df7c4ec6510dd15d558f8581b4872 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sat, 28 Sep 2024 13:10:22 -0700 Subject: [PATCH 05/14] Assembly: factor out element insertion --- app-proto/full-interface/src/assembly.rs | 37 ++++++++++++++++++------ 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/app-proto/full-interface/src/assembly.rs b/app-proto/full-interface/src/assembly.rs index aa4355e..0660d0e 100644 --- a/app-proto/full-interface/src/assembly.rs +++ b/app-proto/full-interface/src/assembly.rs @@ -39,6 +39,25 @@ impl Assembly { } } + // insert an element into the assembly without checking whether we already + // have an element with the same identifier. any element that does have the + // same identifier will get kicked out of the `elements_by_id` index + fn insert_element_unchecked(&self, elt: Element) { + let id = elt.id.clone(); + let key = self.elements.update(|elts| elts.insert(elt)); + self.elements_by_id.update(|elts_by_id| elts_by_id.insert(id, key)); + } + + pub fn try_insert_element(&self, elt: Element) -> bool { + let can_insert = self.elements_by_id.with( + |elts_by_id| !elts_by_id.contains_key(&elt.id) + ); + if can_insert { + self.insert_element_unchecked(elt); + } + can_insert + } + pub fn insert_new_element(&self) { // find the next unused identifier in the default sequence let mut id_num = 1; @@ -51,15 +70,15 @@ impl Assembly { } // create and insert a new element - let elt = Element { - id: id.clone(), - 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]), - constraints: BTreeSet::default() - }; - let key = self.elements.update(|elts| elts.insert(elt)); - self.elements_by_id.update(|elts_by_id| elts_by_id.insert(id, key)); + self.insert_element_unchecked( + Element { + 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]), + constraints: BTreeSet::default() + } + ); } pub fn insert_constraint(&self, constraint: Constraint) { From 28b1ecb8e936de8c2d0b2788815204dd60f261ec Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sat, 28 Sep 2024 13:28:53 -0700 Subject: [PATCH 06/14] App: use element insertion method in test --- app-proto/full-interface/src/main.rs | 68 ++++++++++++++-------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/app-proto/full-interface/src/main.rs b/app-proto/full-interface/src/main.rs index e867ad3..5ed9d30 100644 --- a/app-proto/full-interface/src/main.rs +++ b/app-proto/full-interface/src/main.rs @@ -31,43 +31,43 @@ impl AppState { fn main() { sycamore::render(|| { let state = AppState::new(); - let key_a = state.assembly.elements.update( - |elts| elts.insert( - Element { - id: String::from("wing_a"), - label: String::from("Wing A"), - color: [1.00_f32, 0.25_f32, 0.00_f32], - rep: DVector::::from_column_slice(&[0.5, 0.5, 0.0, 0.5, -0.25]), - constraints: BTreeSet::default() - } - ) + let assemb = &state.assembly; + let _ = assemb.try_insert_element( + Element { + id: String::from("wing_a"), + label: String::from("Wing A"), + color: [1.00_f32, 0.25_f32, 0.00_f32], + rep: DVector::::from_column_slice(&[0.5, 0.5, 0.0, 0.5, -0.25]), + constraints: BTreeSet::default() + } ); - let key_b = state.assembly.elements.update( - |elts| elts.insert( - Element { - id: String::from("wing_b"), - label: String::from("Wing B"), - color: [0.00_f32, 0.25_f32, 1.00_f32], - rep: DVector::::from_column_slice(&[-0.5, -0.5, 0.0, 0.5, -0.25]), - constraints: BTreeSet::default() - }, - ) + let _ = assemb.try_insert_element( + Element { + id: String::from("wing_b"), + label: String::from("Wing B"), + color: [0.00_f32, 0.25_f32, 1.00_f32], + rep: DVector::::from_column_slice(&[-0.5, -0.5, 0.0, 0.5, -0.25]), + constraints: BTreeSet::default() + } ); - state.assembly.elements.update( - |elts| elts.insert( - Element { - id: String::from("central"), - label: String::from("Central"), - color: [0.75_f32, 0.75_f32, 0.75_f32], - rep: DVector::::from_column_slice(&[0.0, 0.0, 0.0, 0.4, -0.625]), - constraints: BTreeSet::default() - } - ) + let _ = assemb.try_insert_element( + Element { + id: String::from("central"), + label: String::from("Central"), + color: [0.75_f32, 0.75_f32, 0.75_f32], + rep: DVector::::from_column_slice(&[0.0, 0.0, 0.0, 0.4, -0.625]), + constraints: BTreeSet::default() + } + ); + assemb.insert_constraint( + Constraint { + args: ( + assemb.elements_by_id.with(|elts_by_id| elts_by_id["wing_a"]), + assemb.elements_by_id.with(|elts_by_id| elts_by_id["wing_b"]) + ), + rep: 0.5 + } ); - state.assembly.insert_constraint(Constraint { - args: (key_a, key_b), - rep: 0.5 - }); provide_context(state); view! { From 4f8f36053fec599fa6617beef34f1df73186e800 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sat, 28 Sep 2024 14:18:04 -0700 Subject: [PATCH 07/14] App: use general test assembly from `inversive-display` This moves us toward dropping the separate display prototype. --- app-proto/full-interface/src/engine.rs | 27 +++++++++++++ app-proto/full-interface/src/main.rs | 52 ++++++++++++++++++++------ 2 files changed, 67 insertions(+), 12 deletions(-) create mode 100644 app-proto/full-interface/src/engine.rs diff --git a/app-proto/full-interface/src/engine.rs b/app-proto/full-interface/src/engine.rs new file mode 100644 index 0000000..79668bb --- /dev/null +++ b/app-proto/full-interface/src/engine.rs @@ -0,0 +1,27 @@ +use nalgebra::DVector; + +// the sphere with the given center and radius, with inward-pointing normals +pub fn sphere(center_x: f64, center_y: f64, center_z: f64, radius: f64) -> DVector { + let center_norm_sq = center_x * center_x + center_y * center_y + center_z * center_z; + DVector::from_column_slice(&[ + center_x / radius, + center_y / radius, + center_z / radius, + 0.5 / radius, + 0.5 * (center_norm_sq / radius - radius) + ]) +} + +// the sphere of curvature `curv` whose closest point to the origin has position +// `off * dir` and normal `dir`, where `dir` is a unit vector. setting the +// curvature to zero gives a plane +pub fn sphere_with_offset(dir_x: f64, dir_y: f64, dir_z: f64, off: f64, curv: f64) -> DVector { + let norm_sp = 1.0 + off * curv; + DVector::from_column_slice(&[ + norm_sp * dir_x, + norm_sp * dir_y, + norm_sp * dir_z, + 0.5 * curv, + off * (1.0 + 0.5 * off * curv) + ]) +} \ No newline at end of file diff --git a/app-proto/full-interface/src/main.rs b/app-proto/full-interface/src/main.rs index 5ed9d30..0ca2209 100644 --- a/app-proto/full-interface/src/main.rs +++ b/app-proto/full-interface/src/main.rs @@ -1,6 +1,7 @@ mod add_remove; mod assembly; mod display; +mod engine; mod outline; use nalgebra::DVector; @@ -34,36 +35,63 @@ fn main() { let assemb = &state.assembly; let _ = assemb.try_insert_element( Element { - id: String::from("wing_a"), - label: String::from("Wing A"), + id: String::from("gemini_a"), + label: String::from("Castor"), color: [1.00_f32, 0.25_f32, 0.00_f32], - rep: DVector::::from_column_slice(&[0.5, 0.5, 0.0, 0.5, -0.25]), + rep: engine::sphere(0.5, 0.5, 0.0, 1.0), constraints: BTreeSet::default() } ); let _ = assemb.try_insert_element( Element { - id: String::from("wing_b"), - label: String::from("Wing B"), + id: String::from("gemini_b"), + label: String::from("Pollux"), color: [0.00_f32, 0.25_f32, 1.00_f32], - rep: DVector::::from_column_slice(&[-0.5, -0.5, 0.0, 0.5, -0.25]), + rep: engine::sphere(-0.5, -0.5, 0.0, 1.0), constraints: BTreeSet::default() } ); let _ = assemb.try_insert_element( Element { - id: String::from("central"), - label: String::from("Central"), - color: [0.75_f32, 0.75_f32, 0.75_f32], - rep: DVector::::from_column_slice(&[0.0, 0.0, 0.0, 0.4, -0.625]), + 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), + constraints: BTreeSet::default() + } + ); + let _ = assemb.try_insert_element( + Element { + 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), + constraints: BTreeSet::default() + } + ); + let _ = assemb.try_insert_element( + Element { + 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), + constraints: BTreeSet::default() + } + ); + let _ = assemb.try_insert_element( + Element { + 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), constraints: BTreeSet::default() } ); assemb.insert_constraint( Constraint { args: ( - assemb.elements_by_id.with(|elts_by_id| elts_by_id["wing_a"]), - assemb.elements_by_id.with(|elts_by_id| elts_by_id["wing_b"]) + assemb.elements_by_id.with(|elts_by_id| elts_by_id["gemini_a"]), + assemb.elements_by_id.with(|elts_by_id| elts_by_id["gemini_b"]) ), rep: 0.5 } From 721a8716d41453c46e89e136f487d11ebd570a2a Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sat, 28 Sep 2024 18:49:17 -0700 Subject: [PATCH 08/14] Assembly: don't track element list when inserting Calling `try_insert_element` or `insert_new_element` in a responsive context shouldn't make the context track `elements_by_id`. --- app-proto/full-interface/src/assembly.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app-proto/full-interface/src/assembly.rs b/app-proto/full-interface/src/assembly.rs index 0660d0e..c0c9959 100644 --- a/app-proto/full-interface/src/assembly.rs +++ b/app-proto/full-interface/src/assembly.rs @@ -49,7 +49,7 @@ impl Assembly { } pub fn try_insert_element(&self, elt: Element) -> bool { - let can_insert = self.elements_by_id.with( + let can_insert = self.elements_by_id.with_untracked( |elts_by_id| !elts_by_id.contains_key(&elt.id) ); if can_insert { @@ -62,7 +62,7 @@ impl Assembly { // find the next unused identifier in the default sequence let mut id_num = 1; let mut id = format!("sphere{}", id_num); - while self.elements_by_id.with( + while self.elements_by_id.with_untracked( |elts_by_id| elts_by_id.contains_key(&id) ) { id_num += 1; From 1c9fec36e5e1bab5c57fc639a60b8af6ee8293e5 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sat, 28 Sep 2024 18:51:28 -0700 Subject: [PATCH 09/14] Display: make scene change flag track element list --- app-proto/full-interface/src/display.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/app-proto/full-interface/src/display.rs b/app-proto/full-interface/src/display.rs index 52b2ae9..67062b7 100644 --- a/app-proto/full-interface/src/display.rs +++ b/app-proto/full-interface/src/display.rs @@ -102,6 +102,7 @@ pub fn Display() -> View { // change listener let scene_changed = create_signal(true); create_effect(move || { + state.assembly.elements.track(); state.selection.track(); scene_changed.set(true); }); From 7977b11caf4ae62bacd19e63c7b437d63380f626 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sat, 28 Sep 2024 18:56:33 -0700 Subject: [PATCH 10/14] AddRemove: switch between pre-made test assemblies --- app-proto/full-interface/src/add_remove.rs | 102 ++++++++++++++++++++- app-proto/full-interface/src/main.rs | 69 +------------- 2 files changed, 102 insertions(+), 69 deletions(-) diff --git a/app-proto/full-interface/src/add_remove.rs b/app-proto/full-interface/src/add_remove.rs index f93f31f..aaab8d0 100644 --- a/app-proto/full-interface/src/add_remove.rs +++ b/app-proto/full-interface/src/add_remove.rs @@ -1,11 +1,105 @@ +use std::collections::BTreeSet; /* DEBUG */ use sycamore::prelude::*; use web_sys::{console, wasm_bindgen::JsValue}; -use crate::AppState; -use crate::Constraint; +use crate::{engine, AppState, assembly::{Assembly, Constraint, Element}}; + +/* DEBUG */ +fn load_gen_assemb(assembly: &Assembly) { + let _ = assembly.try_insert_element( + Element { + 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), + constraints: BTreeSet::default() + } + ); + let _ = assembly.try_insert_element( + Element { + 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), + constraints: BTreeSet::default() + } + ); + let _ = assembly.try_insert_element( + Element { + 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), + constraints: BTreeSet::default() + } + ); + let _ = assembly.try_insert_element( + Element { + 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), + constraints: BTreeSet::default() + } + ); + let _ = assembly.try_insert_element( + Element { + 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), + constraints: BTreeSet::default() + } + ); + let _ = assembly.try_insert_element( + Element { + 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), + constraints: BTreeSet::default() + } + ); + assembly.insert_constraint( + Constraint { + args: ( + assembly.elements_by_id.with_untracked(|elts_by_id| elts_by_id["gemini_a"]), + assembly.elements_by_id.with_untracked(|elts_by_id| elts_by_id["gemini_b"]) + ), + rep: 0.5 + } + ); +} #[component] pub fn AddRemove() -> View { + /* DEBUG */ + let assembly_name = create_signal("general".to_string()); + create_effect(move || { + // get name of chosen assembly + let name = assembly_name.get_clone(); + console::log_1( + &JsValue::from(format!("Showing assembly \"{}\"", name.clone())) + ); + + batch(|| { + let state = use_context::(); + let assembly = &state.assembly; + + // clear state + assembly.elements.update(|elts| elts.clear()); + assembly.elements_by_id.update(|elts_by_id| elts_by_id.clear()); + state.selection.update(|sel| sel.clear()); + + // load assembly + match name.as_str() { + "general" => load_gen_assemb(assembly), + "low-curv" => /*load_low_curv_assemb(state)*/(), + _ => () + }; + }); + }); + view! { div(id="add-remove") { button( @@ -60,6 +154,10 @@ pub fn AddRemove() -> View { }); } ) { "🔗" } + select(bind:value=assembly_name) { /* DEBUG */ + option(value="general") { "General" } + option(value="low-curv") { "Low-curvature" } + } } } } \ No newline at end of file diff --git a/app-proto/full-interface/src/main.rs b/app-proto/full-interface/src/main.rs index 0ca2209..bca0378 100644 --- a/app-proto/full-interface/src/main.rs +++ b/app-proto/full-interface/src/main.rs @@ -10,7 +10,7 @@ use std::collections::BTreeSet; use sycamore::prelude::*; use add_remove::AddRemove; -use assembly::{Assembly, Constraint, Element}; +use assembly::Assembly; use display::Display; use outline::Outline; @@ -31,72 +31,7 @@ impl AppState { fn main() { sycamore::render(|| { - let state = AppState::new(); - let assemb = &state.assembly; - let _ = assemb.try_insert_element( - Element { - 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), - constraints: BTreeSet::default() - } - ); - let _ = assemb.try_insert_element( - Element { - 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), - constraints: BTreeSet::default() - } - ); - let _ = assemb.try_insert_element( - Element { - 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), - constraints: BTreeSet::default() - } - ); - let _ = assemb.try_insert_element( - Element { - 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), - constraints: BTreeSet::default() - } - ); - let _ = assemb.try_insert_element( - Element { - 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), - constraints: BTreeSet::default() - } - ); - let _ = assemb.try_insert_element( - Element { - 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), - constraints: BTreeSet::default() - } - ); - assemb.insert_constraint( - Constraint { - args: ( - assemb.elements_by_id.with(|elts_by_id| elts_by_id["gemini_a"]), - assemb.elements_by_id.with(|elts_by_id| elts_by_id["gemini_b"]) - ), - rep: 0.5 - } - ); - provide_context(state); + provide_context(AppState::new()); view! { div(id="sidebar") { From 25fa108e9b2cfba27918c98fc1d89042fdcf85eb Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sat, 28 Sep 2024 19:37:43 -0700 Subject: [PATCH 11/14] AddRemove: add low-curvature test assembly from `inversive-display` --- app-proto/full-interface/src/add_remove.rs | 79 +++++++++++++++++++++- 1 file changed, 78 insertions(+), 1 deletion(-) diff --git a/app-proto/full-interface/src/add_remove.rs b/app-proto/full-interface/src/add_remove.rs index aaab8d0..40b0e98 100644 --- a/app-proto/full-interface/src/add_remove.rs +++ b/app-proto/full-interface/src/add_remove.rs @@ -71,6 +71,83 @@ fn load_gen_assemb(assembly: &Assembly) { ); } +/* DEBUG */ +fn load_low_curv_assemb(assembly: &Assembly) { + let a = 0.75_f64.sqrt(); + let _ = assembly.try_insert_element( + Element { + 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), + constraints: BTreeSet::default() + } + ); + let _ = assembly.try_insert_element( + Element { + 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), + constraints: BTreeSet::default() + } + ); + let _ = assembly.try_insert_element( + Element { + 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), + constraints: BTreeSet::default() + } + ); + let _ = assembly.try_insert_element( + Element { + 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), + constraints: BTreeSet::default() + } + ); + let _ = assembly.try_insert_element( + Element { + 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), + constraints: BTreeSet::default() + } + ); + let _ = assembly.try_insert_element( + Element { + 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), + constraints: BTreeSet::default() + } + ); + let _ = assembly.try_insert_element( + Element { + 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), + constraints: BTreeSet::default() + } + ); + let _ = assembly.try_insert_element( + Element { + 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), + constraints: BTreeSet::default() + } + ); +} + #[component] pub fn AddRemove() -> View { /* DEBUG */ @@ -94,7 +171,7 @@ pub fn AddRemove() -> View { // load assembly match name.as_str() { "general" => load_gen_assemb(assembly), - "low-curv" => /*load_low_curv_assemb(state)*/(), + "low-curv" => load_low_curv_assemb(assembly), _ => () }; }); From 70bd39b9e5b0808f75ce322be45b2b54c169fa79 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sun, 29 Sep 2024 23:30:35 -0700 Subject: [PATCH 12/14] App: remove unused imports --- app-proto/full-interface/src/main.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/app-proto/full-interface/src/main.rs b/app-proto/full-interface/src/main.rs index bca0378..2c71a83 100644 --- a/app-proto/full-interface/src/main.rs +++ b/app-proto/full-interface/src/main.rs @@ -4,9 +4,7 @@ mod display; mod engine; mod outline; -use nalgebra::DVector; use rustc_hash::FxHashSet; -use std::collections::BTreeSet; use sycamore::prelude::*; use add_remove::AddRemove; From edace8e4eaeb1050399d7b31a8f3762c55f894a7 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sun, 29 Sep 2024 23:41:16 -0700 Subject: [PATCH 13/14] Outline: include ID and label in element diff key --- app-proto/full-interface/src/outline.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app-proto/full-interface/src/outline.rs b/app-proto/full-interface/src/outline.rs index 8a0a3fb..c980887 100644 --- a/app-proto/full-interface/src/outline.rs +++ b/app-proto/full-interface/src/outline.rs @@ -148,7 +148,12 @@ pub fn Outline() -> View { } } }, - key=|(key, elt)| (key.clone(), elt.constraints.clone()) + key=|(key, elt)| ( + key.clone(), + elt.id.clone(), + elt.label.clone(), + elt.constraints.clone() + ) ) } } From 18ebf3be2c8a9caf0cdec3338bd6ecb1333a144e Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Mon, 30 Sep 2024 00:44:13 -0700 Subject: [PATCH 14/14] Display: add turntable for benchmarking Together with 25fa108 and 4f8f360, this lets us do a benchmarking routine for `full-interface` which is comparable to the one we've been using for `inversive-display`. --- app-proto/full-interface/src/display.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app-proto/full-interface/src/display.rs b/app-proto/full-interface/src/display.rs index 67062b7..c32b470 100644 --- a/app-proto/full-interface/src/display.rs +++ b/app-proto/full-interface/src/display.rs @@ -98,6 +98,7 @@ pub fn Display() -> View { let roll_cw = create_signal(0.0); let zoom_in = create_signal(0.0); let zoom_out = create_signal(0.0); + let turntable = create_signal(false); /* BENCHMARKING */ // change listener let scene_changed = create_signal(true); @@ -120,6 +121,7 @@ pub fn Display() -> View { // viewpoint const ROT_SPEED: f64 = 0.4; // in radians per second const ZOOM_SPEED: f64 = 0.15; // multiplicative rate per second + const TURNTABLE_SPEED: f64 = 0.1; /* BENCHMARKING */ let mut orientation = DMatrix::::identity(5, 5); let mut rotation = DMatrix::::identity(5, 5); let mut location_z: f64 = 5.0; @@ -242,6 +244,7 @@ pub fn Display() -> View { let roll_cw_val = roll_cw.get(); let zoom_in_val = zoom_in.get(); let zoom_out_val = zoom_out.get(); + let turntable_val = turntable.get(); /* BENCHMARKING */ // update the assembly's orientation let ang_vel = { @@ -253,6 +256,10 @@ pub fn Display() -> View { } else { Vector3::zeros() } + } /* BENCHMARKING */ + if turntable_val { + Vector3::new(0.0, TURNTABLE_SPEED, 0.0) + } else { + Vector3::zeros() }; let mut rotation_sp = rotation.fixed_view_mut::<3, 3>(0, 0); rotation_sp.copy_from( @@ -352,6 +359,7 @@ pub fn Display() -> View { || roll_ccw_val != 0.0 || zoom_in_val != 0.0 || zoom_out_val != 0.0 + || turntable_val /* BENCHMARKING */ ); } else { frames_since_last_sample = 0; @@ -401,6 +409,10 @@ pub fn Display() -> View { pitch_up.set(0.0); pitch_down.set(0.0); } else { + if event.key() == "Enter" { /* BENCHMARKING */ + turntable.set_fn(|turn| !turn); + scene_changed.set(true); + } set_nav_signal(event, 1.0); } },