diff --git a/app-proto/index.html b/app-proto/index.html index 92238f4..941a153 100644 --- a/app-proto/index.html +++ b/app-proto/index.html @@ -4,7 +4,7 @@ dyna3 - + diff --git a/app-proto/main.css b/app-proto/main.css index 204c8c8..ba8a20b 100644 --- a/app-proto/main.css +++ b/app-proto/main.css @@ -2,7 +2,7 @@ body { margin: 0px; color: #fcfcfc; background-color: #222; - font-family: 'Fira Sans', sans-serif; + font-family: 'Lato'; } /* sidebar */ @@ -36,7 +36,7 @@ body { /* KLUDGE */ #add-remove > button.emoji { - font-family: 'Noto Emoji', sans-serif; + font-family: 'Noto Emoji'; } /* outline */ @@ -102,7 +102,6 @@ details[open]:has(li) .elt-switch::after { .elt-rep > div { padding: 2px 0px 0px 0px; font-size: 10pt; - font-variant-numeric: tabular-nums; text-align: right; width: 56px; } diff --git a/app-proto/src/add_remove.rs b/app-proto/src/add_remove.rs index bea20f9..31e11e6 100644 --- a/app-proto/src/add_remove.rs +++ b/app-proto/src/add_remove.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeSet; /* DEBUG */ use sycamore::prelude::*; use web_sys::{console, wasm_bindgen::JsValue}; @@ -6,52 +7,64 @@ use crate::{engine, AppState, assembly::{Assembly, Constraint, Element}}; /* DEBUG */ fn load_gen_assemb(assembly: &Assembly) { let _ = assembly.try_insert_element( - Element::new( - String::from("gemini_a"), - String::from("Castor"), - [1.00_f32, 0.25_f32, 0.00_f32], - engine::sphere(0.5, 0.5, 0.0, 1.0) - ) + Element { + id: String::from("gemini_a"), + label: String::from("Castor"), + color: [1.00_f32, 0.25_f32, 0.00_f32], + representation: engine::sphere(0.5, 0.5, 0.0, 1.0), + constraints: BTreeSet::default(), + index: 0 + } ); let _ = assembly.try_insert_element( - Element::new( - String::from("gemini_b"), - String::from("Pollux"), - [0.00_f32, 0.25_f32, 1.00_f32], - engine::sphere(-0.5, -0.5, 0.0, 1.0) - ) + Element { + id: String::from("gemini_b"), + label: String::from("Pollux"), + color: [0.00_f32, 0.25_f32, 1.00_f32], + representation: engine::sphere(-0.5, -0.5, 0.0, 1.0), + constraints: BTreeSet::default(), + index: 0 + } ); let _ = assembly.try_insert_element( - Element::new( - String::from("ursa_major"), - String::from("Ursa major"), - [0.25_f32, 0.00_f32, 1.00_f32], - engine::sphere(-0.5, 0.5, 0.0, 0.75) - ) + Element { + id: String::from("ursa_major"), + label: String::from("Ursa major"), + color: [0.25_f32, 0.00_f32, 1.00_f32], + representation: engine::sphere(-0.5, 0.5, 0.0, 0.75), + constraints: BTreeSet::default(), + index: 0 + } ); let _ = assembly.try_insert_element( - Element::new( - String::from("ursa_minor"), - String::from("Ursa minor"), - [0.25_f32, 1.00_f32, 0.00_f32], - engine::sphere(0.5, -0.5, 0.0, 0.5) - ) + Element { + id: String::from("ursa_minor"), + label: String::from("Ursa minor"), + color: [0.25_f32, 1.00_f32, 0.00_f32], + representation: engine::sphere(0.5, -0.5, 0.0, 0.5), + constraints: BTreeSet::default(), + index: 0 + } ); let _ = assembly.try_insert_element( - Element::new( - String::from("moon_deimos"), - String::from("Deimos"), - [0.75_f32, 0.75_f32, 0.00_f32], - engine::sphere(0.0, 0.15, 1.0, 0.25) - ) + Element { + id: String::from("moon_deimos"), + label: String::from("Deimos"), + color: [0.75_f32, 0.75_f32, 0.00_f32], + representation: engine::sphere(0.0, 0.15, 1.0, 0.25), + constraints: BTreeSet::default(), + index: 0 + } ); let _ = assembly.try_insert_element( - Element::new( - String::from("moon_phobos"), - String::from("Phobos"), - [0.00_f32, 0.75_f32, 0.50_f32], - engine::sphere(0.0, -0.15, -1.0, 0.25) - ) + Element { + id: String::from("moon_phobos"), + label: String::from("Phobos"), + color: [0.00_f32, 0.75_f32, 0.50_f32], + representation: engine::sphere(0.0, -0.15, -1.0, 0.25), + constraints: BTreeSet::default(), + index: 0 + } ); } @@ -59,68 +72,84 @@ fn load_gen_assemb(assembly: &Assembly) { fn load_low_curv_assemb(assembly: &Assembly) { let a = 0.75_f64.sqrt(); let _ = assembly.try_insert_element( - Element::new( - "central".to_string(), - "Central".to_string(), - [0.75_f32, 0.75_f32, 0.75_f32], - engine::sphere(0.0, 0.0, 0.0, 1.0) - ) + Element { + id: "central".to_string(), + label: "Central".to_string(), + color: [0.75_f32, 0.75_f32, 0.75_f32], + representation: engine::sphere(0.0, 0.0, 0.0, 1.0), + constraints: BTreeSet::default(), + index: 0 + } ); let _ = assembly.try_insert_element( - Element::new( - "assemb_plane".to_string(), - "Assembly plane".to_string(), - [0.75_f32, 0.75_f32, 0.75_f32], - engine::sphere_with_offset(0.0, 0.0, 1.0, 0.0, 0.0) - ) + Element { + id: "assemb_plane".to_string(), + label: "Assembly plane".to_string(), + color: [0.75_f32, 0.75_f32, 0.75_f32], + representation: engine::sphere_with_offset(0.0, 0.0, 1.0, 0.0, 0.0), + constraints: BTreeSet::default(), + index: 0 + } ); let _ = assembly.try_insert_element( - Element::new( - "side1".to_string(), - "Side 1".to_string(), - [1.00_f32, 0.00_f32, 0.25_f32], - engine::sphere_with_offset(1.0, 0.0, 0.0, 1.0, 0.0) - ) + Element { + id: "side1".to_string(), + label: "Side 1".to_string(), + color: [1.00_f32, 0.00_f32, 0.25_f32], + representation: engine::sphere_with_offset(1.0, 0.0, 0.0, 1.0, 0.0), + constraints: BTreeSet::default(), + index: 0 + } ); let _ = assembly.try_insert_element( - Element::new( - "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) - ) + Element { + id: "side2".to_string(), + label: "Side 2".to_string(), + color: [0.25_f32, 1.00_f32, 0.00_f32], + representation: engine::sphere_with_offset(-0.5, a, 0.0, 1.0, 0.0), + constraints: BTreeSet::default(), + index: 0 + } ); let _ = assembly.try_insert_element( - Element::new( - "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) - ) + Element { + id: "side3".to_string(), + label: "Side 3".to_string(), + color: [0.00_f32, 0.25_f32, 1.00_f32], + representation: engine::sphere_with_offset(-0.5, -a, 0.0, 1.0, 0.0), + constraints: BTreeSet::default(), + index: 0 + } ); let _ = assembly.try_insert_element( - Element::new( - "corner1".to_string(), - "Corner 1".to_string(), - [0.75_f32, 0.75_f32, 0.75_f32], - engine::sphere(-4.0/3.0, 0.0, 0.0, 1.0/3.0) - ) + Element { + id: "corner1".to_string(), + label: "Corner 1".to_string(), + color: [0.75_f32, 0.75_f32, 0.75_f32], + representation: engine::sphere(-4.0/3.0, 0.0, 0.0, 1.0/3.0), + constraints: BTreeSet::default(), + index: 0 + } ); let _ = assembly.try_insert_element( - Element::new( - "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) - ) + Element { + id: "corner2".to_string(), + label: "Corner 2".to_string(), + color: [0.75_f32, 0.75_f32, 0.75_f32], + representation: engine::sphere(2.0/3.0, -4.0/3.0 * a, 0.0, 1.0/3.0), + constraints: BTreeSet::default(), + index: 0 + } ); let _ = assembly.try_insert_element( - Element::new( - 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) - ) + Element { + id: String::from("corner3"), + label: String::from("Corner 3"), + color: [0.75_f32, 0.75_f32, 0.75_f32], + representation: engine::sphere(2.0/3.0, 4.0/3.0 * a, 0.0, 1.0/3.0), + constraints: BTreeSet::default(), + index: 0 + } ); } @@ -187,15 +216,15 @@ pub fn AddRemove() -> View { } ); let lorentz_prod = create_signal(0.0); - let lorentz_prod_valid = create_signal(false); let active = create_signal(true); state.assembly.insert_constraint(Constraint { subjects: subjects, lorentz_prod: lorentz_prod, lorentz_prod_text: create_signal(String::new()), - lorentz_prod_valid: lorentz_prod_valid, + lorentz_prod_valid: create_signal(false), active: active, }); + state.assembly.realize(); state.selection.update(|sel| sel.clear()); /* DEBUG */ @@ -213,14 +242,15 @@ pub fn AddRemove() -> View { } }); - // update the realization when the constraint becomes active - // and valid, or is edited while active and valid + // update the realization when the constraint activated, or + // edited while active create_effect(move || { - console::log_1(&JsValue::from( - format!("Constraint ({}, {}) updated", subjects.0, subjects.1) - )); lorentz_prod.track(); - if active.get() && lorentz_prod_valid.get() { + console::log_2( + &JsValue::from("Lorentz product updated to"), + &JsValue::from(lorentz_prod.get_untracked()) + ); + if active.get() { state.assembly.realize(); } }); diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index b119d5b..0cdf61b 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -18,8 +18,8 @@ pub struct Element { pub id: String, pub label: String, pub color: ElementColor, - pub representation: Signal>, - pub constraints: Signal>, + pub representation: DVector, + pub constraints: BTreeSet, // the configuration matrix column index that was assigned to this element // last time the assembly was realized @@ -29,25 +29,6 @@ pub struct Element { pub index: usize } -impl Element { - pub fn new( - id: String, - label: String, - color: ElementColor, - representation: DVector - ) -> Element { - Element { - id: id, - label: label, - color: color, - representation: create_signal(representation), - constraints: create_signal(BTreeSet::default()), - index: 0 - } - } -} - - #[derive(Clone)] pub struct Constraint { pub subjects: (ElementKey, ElementKey), @@ -111,23 +92,24 @@ impl Assembly { // create and insert a new element self.insert_element_unchecked( - Element::new( - id, - format!("Sphere {}", id_num), - [0.75_f32, 0.75_f32, 0.75_f32], - DVector::::from_column_slice(&[0.0, 0.0, 0.0, 0.5, -0.5]) - ) + Element { + id: id, + label: format!("Sphere {}", id_num), + color: [0.75_f32, 0.75_f32, 0.75_f32], + representation: DVector::::from_column_slice(&[0.0, 0.0, 0.0, 0.5, -0.5]), + constraints: BTreeSet::default(), + index: 0 + } ); } pub fn insert_constraint(&self, constraint: Constraint) { let subjects = constraint.subjects; let key = self.constraints.update(|csts| csts.insert(constraint)); - let subject_constraints = self.elements.with( - |elts| (elts[subjects.0].constraints, elts[subjects.1].constraints) - ); - subject_constraints.0.update(|csts| csts.insert(key)); - subject_constraints.1.update(|csts| csts.insert(key)); + self.elements.update(|elts| { + elts[subjects.0].constraints.insert(key); + elts[subjects.1].constraints.insert(key); + }); } // --- realization --- @@ -161,7 +143,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.representation.get_clone_untracked()); + guess_to_be.set_column(index, &elt.representation); } (gram_to_be, guess_to_be) @@ -203,11 +185,11 @@ impl Assembly { if success { // read out the solution - for (_, elt) in self.elements.get_clone_untracked() { - elt.representation.update( - |rep| rep.set_column(0, &config.column(elt.index)) - ); - } + self.elements.update(|elts| { + for (_, elt) in elts.iter_mut() { + elt.representation.set_column(0, &config.column(elt.index)); + } + }); } } } \ No newline at end of file diff --git a/app-proto/src/display.rs b/app-proto/src/display.rs index ee0af47..79199ec 100644 --- a/app-proto/src/display.rs +++ b/app-proto/src/display.rs @@ -103,11 +103,7 @@ pub fn Display() -> View { // change listener let scene_changed = create_signal(true); create_effect(move || { - state.assembly.elements.with(|elts| { - for (_, elt) in elts { - elt.representation.track(); - } - }); + state.assembly.elements.track(); state.selection.track(); scene_changed.set(true); }); @@ -299,40 +295,25 @@ pub fn Display() -> View { let assembly_to_world = &location * &orientation; // get the assembly - let ( - elt_cnt, - reps_world, - colors, - highlights - ) = state.assembly.elements.with(|elts| { - ( - // number of elements - elts.len() as i32, - - // representation vectors in world coordinates - elts.iter().map( - |(_, elt)| elt.representation.with(|rep| &assembly_to_world * rep) - ).collect::>(), - - // colors - elts.iter().map(|(key, elt)| { - if state.selection.with(|sel| sel.contains(&key)) { - elt.color.map(|ch| 0.2 + 0.8*ch) - } else { - elt.color - } - }).collect::>(), - - // highlight levels - elts.iter().map(|(key, _)| { - if state.selection.with(|sel| sel.contains(&key)) { - 1.0_f32 - } else { - HIGHLIGHT - } - }).collect::>() - ) - }); + 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.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) + } else { + elt.color + } + ).collect(); + let highlights: Vec<_> = element_iter.map(|(key, _)| + if state.selection.with(|sel| sel.contains(&key)) { + 1.0_f32 + } else { + HIGHLIGHT + } + ).collect(); // set the resolution let width = canvas.width() as f32; @@ -341,7 +322,7 @@ pub fn Display() -> View { ctx.uniform1f(shortdim_loc.as_ref(), width.min(height)); // pass the assembly - ctx.uniform1i(sphere_cnt_loc.as_ref(), elt_cnt); + ctx.uniform1i(sphere_cnt_loc.as_ref(), elements.len() as i32); for n in 0..reps_world.len() { let v = &reps_world[n]; ctx.uniform3f( diff --git a/app-proto/src/outline.rs b/app-proto/src/outline.rs index f3951b5..aff3673 100644 --- a/app-proto/src/outline.rs +++ b/app-proto/src/outline.rs @@ -1,5 +1,5 @@ use itertools::Itertools; -use sycamore::prelude::*; +use sycamore::{prelude::*, web::tags::div}; use web_sys::{ Event, HtmlInputElement, @@ -43,9 +43,13 @@ fn ConstraintOutlineItem(constraint_key: ConstraintKey, element_key: ElementKey) constraint.subjects.0 }; let other_subject_label = assembly.elements.with(|elts| elts[other_subject].label.clone()); - let class = constraint.lorentz_prod_valid.map( - |&lorentz_prod_valid| if lorentz_prod_valid { "cst" } else { "cst invalid" } - ); + let class = create_memo(move || { + if constraint.lorentz_prod_valid.get() { + "cst" + } else { + "cst invalid" + } + }); view! { li(class=class.get()) { input(r#type="checkbox", bind:checked=constraint.active) @@ -60,19 +64,19 @@ fn ConstraintOutlineItem(constraint_key: ConstraintKey, element_key: ElementKey) #[component(inline_props)] fn ElementOutlineItem(key: ElementKey, element: assembly::Element) -> View { let state = use_context::(); - let class = state.selection.map( - move |sel| if sel.contains(&key) { "selected" } else { "" } - ); + let class = create_memo(move || { + if state.selection.with(|sel| sel.contains(&key)) { + "selected" + } else { + "" + } + }); let label = element.label.clone(); - let rep_components = element.representation.map( - |rep| rep.iter().map( - |u| format!("{:.3}", u).replace("-", "\u{2212}") - ).collect() - ); - let constrained = element.constraints.map(|csts| csts.len() > 0); - let constraint_list = element.constraints.map( - |csts| csts.clone().into_iter().collect() - ); + let rep_components = element.representation.iter().map(|u| { + let u_coord = format!("{:.3}", u).replace("-", "\u{2212}"); + View::from(div().children(u_coord)) + }).collect::>(); + let constrained = element.constraints.len() > 0; let details_node = create_node_ref(); view! { li { @@ -97,7 +101,7 @@ fn ElementOutlineItem(key: ElementKey, element: assembly::Element) -> View { } event.prevent_default(); }, - "ArrowRight" if constrained.get() => { + "ArrowRight" if constrained => { let _ = details_node .get() .unchecked_into::() @@ -140,20 +144,13 @@ fn ElementOutlineItem(key: ElementKey, element: assembly::Element) -> View { } ) { div(class="elt-label") { (label) } - div(class="elt-rep") { - Indexed( - list=rep_components, - view=|coord_str| view! { - div { (coord_str) } - } - ) - } + div(class="elt-rep") { (rep_components) } div(class="status") } } ul(class="constraints") { Keyed( - list=constraint_list, + list=element.constraints.into_iter().collect::>(), view=move |cst_key| view! { ConstraintOutlineItem( constraint_key=cst_key, @@ -176,16 +173,15 @@ fn ElementOutlineItem(key: ElementKey, element: assembly::Element) -> View { // #[component] pub fn Outline() -> View { - let state = use_context::(); - - // list the elements alphabetically by ID - let element_list = state.assembly.elements.map( - |elts| elts - .clone() + // sort the elements alphabetically by ID + let elements_sorted = create_memo(|| { + let state = use_context::(); + state.assembly.elements + .get_clone() .into_iter() .sorted_by_key(|(_, elt)| elt.id.clone()) .collect() - ); + }); view! { ul( @@ -196,11 +192,16 @@ pub fn Outline() -> View { } ) { Keyed( - list=element_list, + list=elements_sorted, view=|(key, elt)| view! { ElementOutlineItem(key=key, element=elt) }, - key=|(key, _)| key.clone() + key=|(key, elt)| ( + key.clone(), + elt.id.clone(), + elt.label.clone(), + elt.constraints.clone() + ) ) } }