From d526ac9f1ff465e870a4946a36a261806fc709ca Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Wed, 30 Oct 2024 15:49:01 -0700 Subject: [PATCH 01/23] Factor out constraint outline item --- app-proto/src/outline.rs | 47 ++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/app-proto/src/outline.rs b/app-proto/src/outline.rs index f7c975c..0384aaf 100644 --- a/app-proto/src/outline.rs +++ b/app-proto/src/outline.rs @@ -9,7 +9,7 @@ use web_sys::{ wasm_bindgen::JsCast }; -use crate::{AppState, assembly::Constraint}; +use crate::{AppState, assembly::{Constraint, ConstraintKey, ElementKey}}; // an editable view of the Lorentz product representing a constraint #[component(inline_props)] @@ -32,6 +32,27 @@ fn LorentzProductInput(constraint: Constraint) -> View { } } +// a list item that shows a constraint in an outline view of an element +#[component(inline_props)] +fn ConstraintOutlineItem(constraint_key: ConstraintKey, element_key: ElementKey) -> View { + let state = use_context::(); + let assembly = &state.assembly; + let constraint = assembly.constraints.with(|csts| csts[constraint_key].clone()); + let other_subject = if constraint.subjects.0 == element_key { + constraint.subjects.1 + } else { + constraint.subjects.0 + }; + let other_subject_label = assembly.elements.with(|elts| elts[other_subject].label.clone()); + view! { + li(class="cst") { + input(r#type="checkbox", bind:checked=constraint.active) + div(class="cst-label") { (other_subject_label) } + LorentzProductInput(constraint=constraint) + } + } +} + // a component that lists the elements of the current assembly, showing the // constraints on each element as a collapsible sub-list. its implementation // is based on Kate Morley's HTML + CSS tree views: @@ -150,25 +171,13 @@ pub fn Outline() -> View { ul(class="constraints") { Keyed( list=elt.constraints.into_iter().collect::>(), - view=move |c_key| { - 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.subjects.0 == key { - cst.subjects.1 - } else { - cst.subjects.0 - }; - let other_arg_label = assembly.elements.with(|elts| elts[other_arg].label.clone()); - view! { - li(class="cst") { - input(r#type="checkbox", bind:checked=cst.active) - div(class="cst-label") { (other_arg_label) } - LorentzProductInput(constraint=cst) - } - } + view=move |cst_key| view! { + ConstraintOutlineItem( + constraint_key=cst_key, + element_key=key + ) }, - key=|c_key| c_key.clone() + key=|cst_key| cst_key.clone() ) } } -- 2.34.1 From dcf57649936a2913909558205b8a042eb467306b Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Wed, 30 Oct 2024 16:01:19 -0700 Subject: [PATCH 02/23] Factor out element outline item --- app-proto/src/outline.rs | 213 ++++++++++++++++++++------------------- 1 file changed, 109 insertions(+), 104 deletions(-) diff --git a/app-proto/src/outline.rs b/app-proto/src/outline.rs index 0384aaf..fb62f33 100644 --- a/app-proto/src/outline.rs +++ b/app-proto/src/outline.rs @@ -1,7 +1,6 @@ use itertools::Itertools; use sycamore::{prelude::*, web::tags::div}; use web_sys::{ - Element, Event, HtmlInputElement, KeyboardEvent, @@ -9,7 +8,7 @@ use web_sys::{ wasm_bindgen::JsCast }; -use crate::{AppState, assembly::{Constraint, ConstraintKey, ElementKey}}; +use crate::{AppState, assembly, assembly::{Constraint, ConstraintKey, ElementKey}}; // an editable view of the Lorentz product representing a constraint #[component(inline_props)] @@ -53,6 +52,112 @@ fn ConstraintOutlineItem(constraint_key: ConstraintKey, element_key: ElementKey) } } +// a list item that shows an element in an outline view of an assembly +#[component(inline_props)] +fn ElementOutlineItem(key: ElementKey, element: assembly::Element) -> View { + let state = use_context::(); + 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.iter().map(|u| { + let u_coord = u.to_string().replace("-", "\u{2212}"); + View::from(div().children(u_coord)) + }).collect::>(); + let constrained = element.constraints.len() > 0; + let details_node = create_node_ref(); + view! { + li { + details(ref=details_node) { + summary( + class=class.get(), + on:keydown={ + move |event: KeyboardEvent| { + match event.key().as_str() { + "Enter" => { + if event.shift_key() { + state.selection.update(|sel| { + if !sel.remove(&key) { + sel.insert(key); + } + }); + } else { + state.selection.update(|sel| { + sel.clear(); + sel.insert(key); + }); + } + event.prevent_default(); + }, + "ArrowRight" if constrained => { + let _ = details_node + .get() + .unchecked_into::() + .set_attribute("open", ""); + }, + "ArrowLeft" => { + let _ = details_node + .get() + .unchecked_into::() + .remove_attribute("open"); + }, + _ => () + } + } + } + ) { + div( + class="elt-switch", + on:click=|event: MouseEvent| event.stop_propagation() + ) + div( + class="elt", + on:click={ + move |event: MouseEvent| { + if event.shift_key() { + state.selection.update(|sel| { + if !sel.remove(&key) { + sel.insert(key); + } + }); + } else { + state.selection.update(|sel| { + sel.clear(); + sel.insert(key); + }); + } + event.stop_propagation(); + event.prevent_default(); + } + } + ) { + div(class="elt-label") { (label) } + div(class="elt-rep") { (rep_components) } + } + } + ul(class="constraints") { + Keyed( + list=element.constraints.into_iter().collect::>(), + view=move |cst_key| view! { + ConstraintOutlineItem( + constraint_key=cst_key, + element_key=key + ) + }, + key=|cst_key| cst_key.clone() + ) + } + } + } + } +} + // a component that lists the elements of the current assembly, showing the // constraints on each element as a collapsible sub-list. its implementation // is based on Kate Morley's HTML + CSS tree views: @@ -81,108 +186,8 @@ pub fn Outline() -> View { ) { Keyed( list=elements_sorted, - view=|(key, elt)| { - let state = use_context::(); - let class = create_memo({ - move || { - if state.selection.with(|sel| sel.contains(&key)) { - "selected" - } else { - "" - } - } - }); - let label = elt.label.clone(); - let rep_components = elt.representation.iter().map(|u| { - let u_coord = u.to_string().replace("-", "\u{2212}"); - View::from(div().children(u_coord)) - }).collect::>(); - let constrained = elt.constraints.len() > 0; - let details_node = create_node_ref(); - view! { - li { - details(ref=details_node) { - summary( - class=class.get(), - on:keydown={ - move |event: KeyboardEvent| { - match event.key().as_str() { - "Enter" => { - if event.shift_key() { - state.selection.update(|sel| { - if !sel.remove(&key) { - sel.insert(key); - } - }); - } else { - state.selection.update(|sel| { - sel.clear(); - sel.insert(key); - }); - } - event.prevent_default(); - }, - "ArrowRight" if constrained => { - let _ = details_node - .get() - .unchecked_into::() - .set_attribute("open", ""); - }, - "ArrowLeft" => { - let _ = details_node - .get() - .unchecked_into::() - .remove_attribute("open"); - }, - _ => () - } - } - } - ) { - div( - class="elt-switch", - on:click=|event: MouseEvent| event.stop_propagation() - ) - div( - class="elt", - on:click={ - move |event: MouseEvent| { - if event.shift_key() { - state.selection.update(|sel| { - if !sel.remove(&key) { - sel.insert(key); - } - }); - } else { - state.selection.update(|sel| { - sel.clear(); - sel.insert(key); - }); - } - event.stop_propagation(); - event.prevent_default(); - } - } - ) { - div(class="elt-label") { (label) } - div(class="elt-rep") { (rep_components) } - } - } - ul(class="constraints") { - Keyed( - list=elt.constraints.into_iter().collect::>(), - view=move |cst_key| view! { - ConstraintOutlineItem( - constraint_key=cst_key, - element_key=key - ) - }, - key=|cst_key| cst_key.clone() - ) - } - } - } - } + view=|(key, elt)| view! { + ElementOutlineItem(key=key, element=elt) }, key=|(key, elt)| ( key.clone(), -- 2.34.1 From f2f73b31cf4ae4985691a77bfb7e67c3159c1bf8 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Wed, 30 Oct 2024 16:06:38 -0700 Subject: [PATCH 03/23] Update title and authors --- app-proto/Cargo.toml | 4 ++-- app-proto/index.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app-proto/Cargo.toml b/app-proto/Cargo.toml index e5bc05e..e623b26 100644 --- a/app-proto/Cargo.toml +++ b/app-proto/Cargo.toml @@ -1,7 +1,7 @@ [package] -name = "sketch-outline" +name = "dyna3" version = "0.1.0" -authors = ["Aaron"] +authors = ["Aaron Fenyes", "Glen Whitney"] edition = "2021" [features] diff --git a/app-proto/index.html b/app-proto/index.html index 5474fe9..cee4b1b 100644 --- a/app-proto/index.html +++ b/app-proto/index.html @@ -2,7 +2,7 @@ - Sketch outline + dyna3 -- 2.34.1 From 7361f1a72101badeecb047a7b4d86795ead6800c Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Wed, 30 Oct 2024 21:12:40 -0700 Subject: [PATCH 04/23] Flag constraints with invalid input --- app-proto/main.css | 22 +++++++++++++++++++++- app-proto/src/outline.rs | 23 +++++++++++++++-------- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/app-proto/main.css b/app-proto/main.css index 32ae5bf..937714e 100644 --- a/app-proto/main.css +++ b/app-proto/main.css @@ -104,17 +104,37 @@ details[open]:has(li) .elt-switch::after { font-style: italic; } +.cst.invalid { + color: #f58fc2; +} + .cst > input[type=checkbox] { margin: 0px 8px 0px 0px; } .cst > input[type=text] { - color: #fcfcfc; + color: inherit; background-color: inherit; border: 1px solid #555; border-radius: 2px; } +.cst.invalid > input[type=text] { + border-color: #70495c; +} + +.status { + width: 20px; + padding-left: 4px; + text-align: center; + font-style: normal; +} + +.invalid > .status::after, details:has(.invalid):not([open]) .status::after { + content: '⚠'; + color: #f58fc2; +} + /* display */ canvas { diff --git a/app-proto/src/outline.rs b/app-proto/src/outline.rs index fb62f33..f12e7ae 100644 --- a/app-proto/src/outline.rs +++ b/app-proto/src/outline.rs @@ -43,11 +43,19 @@ 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 = create_memo(move || { + if constraint.lorentz_prod_valid.get() { + "cst" + } else { + "cst invalid" + } + }); view! { - li(class="cst") { + li(class=class.get()) { input(r#type="checkbox", bind:checked=constraint.active) div(class="cst-label") { (other_subject_label) } LorentzProductInput(constraint=constraint) + div(class="status") } } } @@ -56,13 +64,11 @@ 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 = create_memo({ - move || { - if state.selection.with(|sel| 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(); @@ -139,6 +145,7 @@ fn ElementOutlineItem(key: ElementKey, element: assembly::Element) -> View { ) { div(class="elt-label") { (label) } div(class="elt-rep") { (rep_components) } + div(class="status") } } ul(class="constraints") { -- 2.34.1 From 4ecb39e73a51aeb8219be917b07ab12544fe0c0e Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Wed, 30 Oct 2024 23:29:48 -0700 Subject: [PATCH 05/23] Specify fonts This should help the interface look more consistent across platforms. The font choices are just placeholders: consistency is the main goal. --- app-proto/index.html | 2 ++ app-proto/main.css | 7 +++++++ app-proto/src/add_remove.rs | 1 + 3 files changed, 10 insertions(+) diff --git a/app-proto/index.html b/app-proto/index.html index cee4b1b..941a153 100644 --- a/app-proto/index.html +++ b/app-proto/index.html @@ -4,6 +4,8 @@ dyna3 + + diff --git a/app-proto/main.css b/app-proto/main.css index 937714e..030f56a 100644 --- a/app-proto/main.css +++ b/app-proto/main.css @@ -2,6 +2,7 @@ body { margin: 0px; color: #fcfcfc; background-color: #222; + font-family: 'Lato'; } /* sidebar */ @@ -33,6 +34,11 @@ body { font-size: large; } +/* KLUDGE */ +#add-remove > button.emoji { + font-family: 'Noto Emoji'; +} + /* outline */ #outline { @@ -127,6 +133,7 @@ details[open]:has(li) .elt-switch::after { width: 20px; padding-left: 4px; text-align: center; + font-family: 'Noto Emoji'; font-style: normal; } diff --git a/app-proto/src/add_remove.rs b/app-proto/src/add_remove.rs index 19b4b8d..31e11e6 100644 --- a/app-proto/src/add_remove.rs +++ b/app-proto/src/add_remove.rs @@ -202,6 +202,7 @@ pub fn AddRemove() -> View { } ) { "+" } button( + class="emoji", /* KLUDGE */ disabled={ let state = use_context::(); state.selection.with(|sel| sel.len() != 2) -- 2.34.1 From db0a8c2da858cfd86f7206ce48d09bbf3ffd3aad Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Wed, 30 Oct 2024 23:57:15 -0700 Subject: [PATCH 06/23] Round element vectors to three decimal places --- app-proto/main.css | 2 +- app-proto/src/outline.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app-proto/main.css b/app-proto/main.css index 030f56a..ba8a20b 100644 --- a/app-proto/main.css +++ b/app-proto/main.css @@ -102,7 +102,7 @@ details[open]:has(li) .elt-switch::after { .elt-rep > div { padding: 2px 0px 0px 0px; font-size: 10pt; - text-align: center; + text-align: right; width: 56px; } diff --git a/app-proto/src/outline.rs b/app-proto/src/outline.rs index f12e7ae..aff3673 100644 --- a/app-proto/src/outline.rs +++ b/app-proto/src/outline.rs @@ -73,7 +73,7 @@ fn ElementOutlineItem(key: ElementKey, element: assembly::Element) -> View { }); let label = element.label.clone(); let rep_components = element.representation.iter().map(|u| { - let u_coord = u.to_string().replace("-", "\u{2212}"); + let u_coord = format!("{:.3}", u).replace("-", "\u{2212}"); View::from(div().children(u_coord)) }).collect::>(); let constrained = element.constraints.len() > 0; -- 2.34.1 From 70978c640b412536fac48976f22775bee7178e66 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Thu, 31 Oct 2024 01:23:22 -0700 Subject: [PATCH 07/23] Include vector representation in element diff key --- app-proto/src/outline.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/app-proto/src/outline.rs b/app-proto/src/outline.rs index aff3673..e1bc3ab 100644 --- a/app-proto/src/outline.rs +++ b/app-proto/src/outline.rs @@ -200,6 +200,7 @@ pub fn Outline() -> View { key.clone(), elt.id.clone(), elt.label.clone(), + elt.rep.into_iter().map(|u| u.to_bits()).collect::>(), elt.constraints.clone() ) ) -- 2.34.1 From cc126fc5272516b6d4784784f250384d80ae3951 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Thu, 31 Oct 2024 01:24:06 -0700 Subject: [PATCH 08/23] Correct typo in comment --- app-proto/src/add_remove.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app-proto/src/add_remove.rs b/app-proto/src/add_remove.rs index 31e11e6..5595b03 100644 --- a/app-proto/src/add_remove.rs +++ b/app-proto/src/add_remove.rs @@ -242,8 +242,8 @@ pub fn AddRemove() -> View { } }); - // update the realization when the constraint activated, or - // edited while active + // update the realization when the constraint is activated, + // or edited while active create_effect(move || { lorentz_prod.track(); console::log_2( -- 2.34.1 From ce9b114dd60c105d6ad66340a93210d4fc80f65b Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Fri, 1 Nov 2024 04:25:03 -0700 Subject: [PATCH 09/23] Render constraint lists dynamically --- app-proto/src/add_remove.rs | 28 ++++++++++++++-------------- app-proto/src/assembly.rs | 13 +++++++------ app-proto/src/outline.rs | 12 +++++++----- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/app-proto/src/add_remove.rs b/app-proto/src/add_remove.rs index 5595b03..fce4c37 100644 --- a/app-proto/src/add_remove.rs +++ b/app-proto/src/add_remove.rs @@ -12,7 +12,7 @@ fn load_gen_assemb(assembly: &Assembly) { 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(), + constraints: create_signal(BTreeSet::default()), index: 0 } ); @@ -22,7 +22,7 @@ fn load_gen_assemb(assembly: &Assembly) { 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(), + constraints: create_signal(BTreeSet::default()), index: 0 } ); @@ -32,7 +32,7 @@ fn load_gen_assemb(assembly: &Assembly) { 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(), + constraints: create_signal(BTreeSet::default()), index: 0 } ); @@ -42,7 +42,7 @@ fn load_gen_assemb(assembly: &Assembly) { 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(), + constraints: create_signal(BTreeSet::default()), index: 0 } ); @@ -52,7 +52,7 @@ fn load_gen_assemb(assembly: &Assembly) { 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(), + constraints: create_signal(BTreeSet::default()), index: 0 } ); @@ -62,7 +62,7 @@ fn load_gen_assemb(assembly: &Assembly) { 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(), + constraints: create_signal(BTreeSet::default()), index: 0 } ); @@ -77,7 +77,7 @@ fn load_low_curv_assemb(assembly: &Assembly) { 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(), + constraints: create_signal(BTreeSet::default()), index: 0 } ); @@ -87,7 +87,7 @@ fn load_low_curv_assemb(assembly: &Assembly) { 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(), + constraints: create_signal(BTreeSet::default()), index: 0 } ); @@ -97,7 +97,7 @@ fn load_low_curv_assemb(assembly: &Assembly) { 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(), + constraints: create_signal(BTreeSet::default()), index: 0 } ); @@ -107,7 +107,7 @@ fn load_low_curv_assemb(assembly: &Assembly) { 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(), + constraints: create_signal(BTreeSet::default()), index: 0 } ); @@ -117,7 +117,7 @@ fn load_low_curv_assemb(assembly: &Assembly) { 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(), + constraints: create_signal(BTreeSet::default()), index: 0 } ); @@ -127,7 +127,7 @@ fn load_low_curv_assemb(assembly: &Assembly) { 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(), + constraints: create_signal(BTreeSet::default()), index: 0 } ); @@ -137,7 +137,7 @@ fn load_low_curv_assemb(assembly: &Assembly) { 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(), + constraints: create_signal(BTreeSet::default()), index: 0 } ); @@ -147,7 +147,7 @@ fn load_low_curv_assemb(assembly: &Assembly) { 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(), + constraints: create_signal(BTreeSet::default()), index: 0 } ); diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index 0cdf61b..2763ae1 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -19,7 +19,7 @@ pub struct Element { pub label: String, pub color: ElementColor, pub representation: DVector, - pub constraints: BTreeSet, + pub constraints: Signal>, // the configuration matrix column index that was assigned to this element // last time the assembly was realized @@ -97,7 +97,7 @@ impl Assembly { 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(), + constraints: create_signal(BTreeSet::default()), index: 0 } ); @@ -106,10 +106,11 @@ impl Assembly { pub fn insert_constraint(&self, constraint: Constraint) { let subjects = constraint.subjects; let key = self.constraints.update(|csts| csts.insert(constraint)); - self.elements.update(|elts| { - elts[subjects.0].constraints.insert(key); - elts[subjects.1].constraints.insert(key); - }); + 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)); } // --- realization --- diff --git a/app-proto/src/outline.rs b/app-proto/src/outline.rs index e1bc3ab..329fc58 100644 --- a/app-proto/src/outline.rs +++ b/app-proto/src/outline.rs @@ -76,7 +76,10 @@ fn ElementOutlineItem(key: ElementKey, element: assembly::Element) -> View { let u_coord = format!("{:.3}", u).replace("-", "\u{2212}"); View::from(div().children(u_coord)) }).collect::>(); - let constrained = element.constraints.len() > 0; + let constrained = element.constraints.map(|csts| csts.len() > 0); + let constraint_list = element.constraints.map( + |csts| csts.clone().into_iter().collect() + ); let details_node = create_node_ref(); view! { li { @@ -101,7 +104,7 @@ fn ElementOutlineItem(key: ElementKey, element: assembly::Element) -> View { } event.prevent_default(); }, - "ArrowRight" if constrained => { + "ArrowRight" if constrained.get() => { let _ = details_node .get() .unchecked_into::() @@ -150,7 +153,7 @@ fn ElementOutlineItem(key: ElementKey, element: assembly::Element) -> View { } ul(class="constraints") { Keyed( - list=element.constraints.into_iter().collect::>(), + list=constraint_list, view=move |cst_key| view! { ConstraintOutlineItem( constraint_key=cst_key, @@ -200,8 +203,7 @@ pub fn Outline() -> View { key.clone(), elt.id.clone(), elt.label.clone(), - elt.rep.into_iter().map(|u| u.to_bits()).collect::>(), - elt.constraints.clone() + elt.representation.into_iter().map(|u| u.to_bits()).collect::>() ) ) } -- 2.34.1 From ec1911b8899c4f925e47c59ded71aa8f259ae3d0 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Fri, 1 Nov 2024 04:43:30 -0700 Subject: [PATCH 10/23] Simplify memos --- app-proto/src/outline.rs | 35 ++++++++++++++--------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/app-proto/src/outline.rs b/app-proto/src/outline.rs index 329fc58..65e440f 100644 --- a/app-proto/src/outline.rs +++ b/app-proto/src/outline.rs @@ -43,13 +43,9 @@ 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 = create_memo(move || { - if constraint.lorentz_prod_valid.get() { - "cst" - } else { - "cst invalid" - } - }); + let class = constraint.lorentz_prod_valid.map( + |&lorentz_prod_valid| if lorentz_prod_valid { "cst" } else { "cst invalid" } + ); view! { li(class=class.get()) { input(r#type="checkbox", bind:checked=constraint.active) @@ -64,13 +60,9 @@ 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 = create_memo(move || { - if state.selection.with(|sel| sel.contains(&key)) { - "selected" - } else { - "" - } - }); + let class = state.selection.map( + move |sel| if sel.contains(&key) { "selected" } else { "" } + ); let label = element.label.clone(); let rep_components = element.representation.iter().map(|u| { let u_coord = format!("{:.3}", u).replace("-", "\u{2212}"); @@ -176,15 +168,16 @@ fn ElementOutlineItem(key: ElementKey, element: assembly::Element) -> View { // #[component] pub fn Outline() -> View { - // sort the elements alphabetically by ID - let elements_sorted = create_memo(|| { - let state = use_context::(); - state.assembly.elements - .get_clone() + let state = use_context::(); + + // list the elements alphabetically by ID + let element_list = state.assembly.elements.map( + |elts| elts + .clone() .into_iter() .sorted_by_key(|(_, elt)| elt.id.clone()) .collect() - }); + ); view! { ul( @@ -195,7 +188,7 @@ pub fn Outline() -> View { } ) { Keyed( - list=elements_sorted, + list=element_list, view=|(key, elt)| view! { ElementOutlineItem(key=key, element=elt) }, -- 2.34.1 From 037a0c376f3806d2dbde540433d7deff811a3869 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Fri, 1 Nov 2024 05:07:34 -0700 Subject: [PATCH 11/23] Add an element constructor --- app-proto/src/add_remove.rs | 197 +++++++++++++++--------------------- app-proto/src/assembly.rs | 33 ++++-- 2 files changed, 109 insertions(+), 121 deletions(-) diff --git a/app-proto/src/add_remove.rs b/app-proto/src/add_remove.rs index fce4c37..7ee20aa 100644 --- a/app-proto/src/add_remove.rs +++ b/app-proto/src/add_remove.rs @@ -1,4 +1,3 @@ -use std::collections::BTreeSet; /* DEBUG */ use sycamore::prelude::*; use web_sys::{console, wasm_bindgen::JsValue}; @@ -7,64 +6,52 @@ 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], - representation: engine::sphere(0.5, 0.5, 0.0, 1.0), - constraints: create_signal(BTreeSet::default()), - index: 0 - } + 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) + ) ); 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], - representation: engine::sphere(-0.5, -0.5, 0.0, 1.0), - constraints: create_signal(BTreeSet::default()), - index: 0 - } + 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) + ) ); 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], - representation: engine::sphere(-0.5, 0.5, 0.0, 0.75), - constraints: create_signal(BTreeSet::default()), - index: 0 - } + 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) + ) ); 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], - representation: engine::sphere(0.5, -0.5, 0.0, 0.5), - constraints: create_signal(BTreeSet::default()), - index: 0 - } + 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) + ) ); 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], - representation: engine::sphere(0.0, 0.15, 1.0, 0.25), - constraints: create_signal(BTreeSet::default()), - index: 0 - } + 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) + ) ); 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], - representation: engine::sphere(0.0, -0.15, -1.0, 0.25), - constraints: create_signal(BTreeSet::default()), - index: 0 - } + 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) + ) ); } @@ -72,84 +59,68 @@ 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 { - 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: create_signal(BTreeSet::default()), - index: 0 - } + 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) + ) ); 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], - representation: engine::sphere_with_offset(0.0, 0.0, 1.0, 0.0, 0.0), - constraints: create_signal(BTreeSet::default()), - index: 0 - } + 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) + ) ); 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], - representation: engine::sphere_with_offset(1.0, 0.0, 0.0, 1.0, 0.0), - constraints: create_signal(BTreeSet::default()), - index: 0 - } + 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) + ) ); 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], - representation: engine::sphere_with_offset(-0.5, a, 0.0, 1.0, 0.0), - constraints: create_signal(BTreeSet::default()), - index: 0 - } + 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) + ) ); 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], - representation: engine::sphere_with_offset(-0.5, -a, 0.0, 1.0, 0.0), - constraints: create_signal(BTreeSet::default()), - index: 0 - } + 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) + ) ); 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], - representation: engine::sphere(-4.0/3.0, 0.0, 0.0, 1.0/3.0), - constraints: create_signal(BTreeSet::default()), - index: 0 - } + 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) + ) ); 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], - representation: engine::sphere(2.0/3.0, -4.0/3.0 * a, 0.0, 1.0/3.0), - constraints: create_signal(BTreeSet::default()), - index: 0 - } + 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) + ) ); 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], - representation: engine::sphere(2.0/3.0, 4.0/3.0 * a, 0.0, 1.0/3.0), - constraints: create_signal(BTreeSet::default()), - index: 0 - } + 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) + ) ); } diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index 2763ae1..eb850d8 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -29,6 +29,25 @@ 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: representation, + constraints: create_signal(BTreeSet::default()), + index: 0 + } + } +} + + #[derive(Clone)] pub struct Constraint { pub subjects: (ElementKey, ElementKey), @@ -92,14 +111,12 @@ impl Assembly { // create and insert a new element self.insert_element_unchecked( - 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: create_signal(BTreeSet::default()), - index: 0 - } + 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]) + ) ); } -- 2.34.1 From 33dd5dbe82bf7bd05bb99a55ca0710f205c7b3ee Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Fri, 1 Nov 2024 19:01:14 -0700 Subject: [PATCH 12/23] Make element vectors reactive --- app-proto/src/assembly.rs | 16 +++++----- app-proto/src/display.rs | 61 +++++++++++++++++++++++++-------------- app-proto/src/outline.rs | 27 +++++++++-------- 3 files changed, 63 insertions(+), 41 deletions(-) diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index eb850d8..78228cc 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -18,7 +18,7 @@ pub struct Element { pub id: String, pub label: String, pub color: ElementColor, - pub representation: DVector, + pub representation: Signal>, pub constraints: Signal>, // the configuration matrix column index that was assigned to this element @@ -40,7 +40,7 @@ impl Element { id: id, label: label, color: color, - representation: representation, + representation: create_signal(representation), constraints: create_signal(BTreeSet::default()), index: 0 } @@ -161,7 +161,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); + guess_to_be.set_column(index, &elt.representation.get_clone()); } (gram_to_be, guess_to_be) @@ -203,11 +203,11 @@ impl Assembly { if success { // read out the solution - self.elements.update(|elts| { - for (_, elt) in elts.iter_mut() { - elt.representation.set_column(0, &config.column(elt.index)); - } - }); + for (_, elt) in self.elements.get_clone() { + elt.representation.update( + |rep| rep.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 79199ec..ee0af47 100644 --- a/app-proto/src/display.rs +++ b/app-proto/src/display.rs @@ -103,7 +103,11 @@ pub fn Display() -> View { // change listener let scene_changed = create_signal(true); create_effect(move || { - state.assembly.elements.track(); + state.assembly.elements.with(|elts| { + for (_, elt) in elts { + elt.representation.track(); + } + }); state.selection.track(); scene_changed.set(true); }); @@ -295,25 +299,40 @@ pub fn Display() -> View { let assembly_to_world = &location * &orientation; // 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.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(); + 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::>() + ) + }); // set the resolution let width = canvas.width() as f32; @@ -322,7 +341,7 @@ pub fn Display() -> View { ctx.uniform1f(shortdim_loc.as_ref(), width.min(height)); // pass the assembly - ctx.uniform1i(sphere_cnt_loc.as_ref(), elements.len() as i32); + ctx.uniform1i(sphere_cnt_loc.as_ref(), elt_cnt); 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 65e440f..f3951b5 100644 --- a/app-proto/src/outline.rs +++ b/app-proto/src/outline.rs @@ -1,5 +1,5 @@ use itertools::Itertools; -use sycamore::{prelude::*, web::tags::div}; +use sycamore::prelude::*; use web_sys::{ Event, HtmlInputElement, @@ -64,10 +64,11 @@ fn ElementOutlineItem(key: ElementKey, element: assembly::Element) -> View { move |sel| if sel.contains(&key) { "selected" } else { "" } ); let label = element.label.clone(); - 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 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() @@ -139,7 +140,14 @@ fn ElementOutlineItem(key: ElementKey, element: assembly::Element) -> View { } ) { div(class="elt-label") { (label) } - div(class="elt-rep") { (rep_components) } + div(class="elt-rep") { + Indexed( + list=rep_components, + view=|coord_str| view! { + div { (coord_str) } + } + ) + } div(class="status") } } @@ -192,12 +200,7 @@ pub fn Outline() -> View { view=|(key, elt)| view! { ElementOutlineItem(key=key, element=elt) }, - key=|(key, elt)| ( - key.clone(), - elt.id.clone(), - elt.label.clone(), - elt.representation.into_iter().map(|u| u.to_bits()).collect::>() - ) + key=|(key, _)| key.clone() ) } } -- 2.34.1 From 92b91df4357caf5abe8b396131c3ce15f7c7f5c0 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Fri, 1 Nov 2024 19:11:33 -0700 Subject: [PATCH 13/23] Use tabular numbers for element vectors --- app-proto/main.css | 1 + 1 file changed, 1 insertion(+) diff --git a/app-proto/main.css b/app-proto/main.css index ba8a20b..80b55f2 100644 --- a/app-proto/main.css +++ b/app-proto/main.css @@ -102,6 +102,7 @@ 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; } -- 2.34.1 From b94280c456402700252f0c51fb92598f41fa68b8 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Fri, 1 Nov 2024 20:40:25 -0700 Subject: [PATCH 14/23] Test representation validity in realization effect --- app-proto/src/add_remove.rs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/app-proto/src/add_remove.rs b/app-proto/src/add_remove.rs index 7ee20aa..bea20f9 100644 --- a/app-proto/src/add_remove.rs +++ b/app-proto/src/add_remove.rs @@ -187,15 +187,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: create_signal(false), + lorentz_prod_valid: lorentz_prod_valid, active: active, }); - state.assembly.realize(); state.selection.update(|sel| sel.clear()); /* DEBUG */ @@ -213,15 +213,14 @@ pub fn AddRemove() -> View { } }); - // update the realization when the constraint is activated, - // or edited while active + // update the realization when the constraint becomes active + // and valid, or is edited while active and valid create_effect(move || { + console::log_1(&JsValue::from( + format!("Constraint ({}, {}) updated", subjects.0, subjects.1) + )); lorentz_prod.track(); - console::log_2( - &JsValue::from("Lorentz product updated to"), - &JsValue::from(lorentz_prod.get_untracked()) - ); - if active.get() { + if active.get() && lorentz_prod_valid.get() { state.assembly.realize(); } }); -- 2.34.1 From d223df869cc928769ac848665bc35d3bf8b29adf Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Fri, 1 Nov 2024 20:49:00 -0700 Subject: [PATCH 15/23] Stop `Assembly::realize` from reacting to itself Previously, `realize` both tracked and updated the element vectors, so calling it in a reactive context could start a feedback loop. --- app-proto/src/assembly.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index 78228cc..b119d5b 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -161,7 +161,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()); + guess_to_be.set_column(index, &elt.representation.get_clone_untracked()); } (gram_to_be, guess_to_be) @@ -203,7 +203,7 @@ impl Assembly { if success { // read out the solution - for (_, elt) in self.elements.get_clone() { + for (_, elt) in self.elements.get_clone_untracked() { elt.representation.update( |rep| rep.set_column(0, &config.column(elt.index)) ); -- 2.34.1 From 102f40055391e27956b05d2607bd67c00884d328 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Fri, 1 Nov 2024 23:58:45 -0700 Subject: [PATCH 16/23] Switch font to Fira Sans It has tabular numbers, and it's nice and big too. --- app-proto/index.html | 2 +- app-proto/main.css | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app-proto/index.html b/app-proto/index.html index 941a153..92238f4 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 80b55f2..204c8c8 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: 'Lato'; + font-family: 'Fira Sans', sans-serif; } /* sidebar */ @@ -36,7 +36,7 @@ body { /* KLUDGE */ #add-remove > button.emoji { - font-family: 'Noto Emoji'; + font-family: 'Noto Emoji', sans-serif; } /* outline */ -- 2.34.1 From b3470b597dee3f68e9d9e685ce1974e91eb79251 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Tue, 12 Nov 2024 23:51:37 -0800 Subject: [PATCH 17/23] Make `Element::index` private --- app-proto/src/assembly.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index b119d5b..9a4dd94 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -23,10 +23,7 @@ pub struct Element { // 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 + index: usize } impl Element { -- 2.34.1 From 882286c0e3216f6063758bb8f6ef6ea23a8629d4 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Wed, 13 Nov 2024 16:36:03 -0800 Subject: [PATCH 18/23] Write out "constraint" in HTML element classes --- app-proto/main.css | 16 ++++++++-------- app-proto/src/outline.rs | 4 ++-- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app-proto/main.css b/app-proto/main.css index 204c8c8..a78efd9 100644 --- a/app-proto/main.css +++ b/app-proto/main.css @@ -61,12 +61,12 @@ summary.selected { background-color: #444; } -summary > div, .cst { +summary > div, .constraint { padding-top: 4px; padding-bottom: 4px; } -.elt, .cst { +.elt, .constraint { display: flex; flex-grow: 1; padding-left: 8px; @@ -91,7 +91,7 @@ details[open]:has(li) .elt-switch::after { flex-grow: 1; } -.cst-label { +.constraint-label { flex-grow: 1; } @@ -107,26 +107,26 @@ details[open]:has(li) .elt-switch::after { width: 56px; } -.cst { +.constraint { font-style: italic; } -.cst.invalid { +.constraint.invalid { color: #f58fc2; } -.cst > input[type=checkbox] { +.constraint > input[type=checkbox] { margin: 0px 8px 0px 0px; } -.cst > input[type=text] { +.constraint > input[type=text] { color: inherit; background-color: inherit; border: 1px solid #555; border-radius: 2px; } -.cst.invalid > input[type=text] { +.constraint.invalid > input[type=text] { border-color: #70495c; } diff --git a/app-proto/src/outline.rs b/app-proto/src/outline.rs index f3951b5..8f2e6d7 100644 --- a/app-proto/src/outline.rs +++ b/app-proto/src/outline.rs @@ -44,12 +44,12 @@ fn ConstraintOutlineItem(constraint_key: ConstraintKey, element_key: ElementKey) }; 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" } + |&lorentz_prod_valid| if lorentz_prod_valid { "constraint" } else { "constraint invalid" } ); view! { li(class=class.get()) { input(r#type="checkbox", bind:checked=constraint.active) - div(class="cst-label") { (other_subject_label) } + div(class="constraint-label") { (other_subject_label) } LorentzProductInput(constraint=constraint) div(class="status") } -- 2.34.1 From 3f3c1739cb42dde22c4b20f9198e73d793e3d2a3 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Wed, 13 Nov 2024 16:40:39 -0800 Subject: [PATCH 19/23] Write out "element" in HTML element classes Write out "representation" too. --- app-proto/main.css | 14 +++++++------- app-proto/src/outline.rs | 8 ++++---- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/app-proto/main.css b/app-proto/main.css index a78efd9..26cdac0 100644 --- a/app-proto/main.css +++ b/app-proto/main.css @@ -66,28 +66,28 @@ summary > div, .constraint { padding-bottom: 4px; } -.elt, .constraint { +.element, .constraint { display: flex; flex-grow: 1; padding-left: 8px; padding-right: 8px; } -.elt-switch { +.element-switch { width: 18px; padding-left: 2px; text-align: center; } -details:has(li) .elt-switch::after { +details:has(li) .element-switch::after { content: '▸'; } -details[open]:has(li) .elt-switch::after { +details[open]:has(li) .element-switch::after { content: '▾'; } -.elt-label { +.element-label { flex-grow: 1; } @@ -95,11 +95,11 @@ details[open]:has(li) .elt-switch::after { flex-grow: 1; } -.elt-rep { +.element-representation { display: flex; } -.elt-rep > div { +.element-representation > div { padding: 2px 0px 0px 0px; font-size: 10pt; font-variant-numeric: tabular-nums; diff --git a/app-proto/src/outline.rs b/app-proto/src/outline.rs index 8f2e6d7..ee1603f 100644 --- a/app-proto/src/outline.rs +++ b/app-proto/src/outline.rs @@ -115,11 +115,11 @@ fn ElementOutlineItem(key: ElementKey, element: assembly::Element) -> View { } ) { div( - class="elt-switch", + class="element-switch", on:click=|event: MouseEvent| event.stop_propagation() ) div( - class="elt", + class="element", on:click={ move |event: MouseEvent| { if event.shift_key() { @@ -139,8 +139,8 @@ fn ElementOutlineItem(key: ElementKey, element: assembly::Element) -> View { } } ) { - div(class="elt-label") { (label) } - div(class="elt-rep") { + div(class="element-label") { (label) } + div(class="element-representation") { Indexed( list=rep_components, view=|coord_str| view! { -- 2.34.1 From 0c8022d78e1f985173d020e54b8494ce151f2fea Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Thu, 14 Nov 2024 00:06:55 -0800 Subject: [PATCH 20/23] Give CSS colors meaningful names --- app-proto/main.css | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/app-proto/main.css b/app-proto/main.css index 26cdac0..d7a71aa 100644 --- a/app-proto/main.css +++ b/app-proto/main.css @@ -1,7 +1,19 @@ +:root { + --text: #fcfcfc; /* almost white */ + --text-bright: white; + --text-invalid: #f58fc2; /* bright pink */ + --border: #555; /* light gray */ + --border-focus: #aaa; /* bright gray */ + --border-invalid: #70495c; /* dusky pink */ + --selection-highlight: #444; /* medium gray */ + --page-background: #222; /* dark gray */ + --display-background: #020202; /* almost black */ +} + body { margin: 0px; - color: #fcfcfc; - background-color: #222; + color: var(--text); + background-color: var(--page-background); font-family: 'Fira Sans', sans-serif; } @@ -17,7 +29,7 @@ body { padding: 0px; border-width: 0px 1px 0px 0px; border-style: solid; - border-color: #555; + border-color: var(--border); } /* add-remove */ @@ -57,8 +69,8 @@ summary { } summary.selected { - color: #fff; - background-color: #444; + color: var(--text-bright); + background-color: var(--selection-highlight); } summary > div, .constraint { @@ -112,7 +124,7 @@ details[open]:has(li) .element-switch::after { } .constraint.invalid { - color: #f58fc2; + color: var(--text-invalid); } .constraint > input[type=checkbox] { @@ -122,12 +134,12 @@ details[open]:has(li) .element-switch::after { .constraint > input[type=text] { color: inherit; background-color: inherit; - border: 1px solid #555; + border: 1px solid var(--border); border-radius: 2px; } .constraint.invalid > input[type=text] { - border-color: #70495c; + border-color: var(--border-invalid); } .status { @@ -140,7 +152,7 @@ details[open]:has(li) .element-switch::after { .invalid > .status::after, details:has(.invalid):not([open]) .status::after { content: '⚠'; - color: #f58fc2; + color: var(--text-invalid); } /* display */ @@ -149,11 +161,11 @@ canvas { float: left; margin-left: 20px; margin-top: 20px; - background-color: #020202; - border: 1px solid #555; + background-color: var(--display-background); + border: 1px solid var(--border); border-radius: 16px; } canvas:focus { - border-color: #aaa; + border-color: var(--border-focus); } \ No newline at end of file -- 2.34.1 From 2b083be998188187bf59ace622669a076a576f74 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Thu, 14 Nov 2024 00:16:32 -0800 Subject: [PATCH 21/23] Explain emoji icon kludge --- app-proto/main.css | 4 ++++ app-proto/src/add_remove.rs | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app-proto/main.css b/app-proto/main.css index d7a71aa..b9fc0a1 100644 --- a/app-proto/main.css +++ b/app-proto/main.css @@ -47,6 +47,10 @@ body { } /* KLUDGE */ +/* + for convenience, we're using emoji as temporary icons for some buttons. these + buttons need to be displayed in an emoji font +*/ #add-remove > button.emoji { font-family: 'Noto Emoji', sans-serif; } diff --git a/app-proto/src/add_remove.rs b/app-proto/src/add_remove.rs index bea20f9..5590b6c 100644 --- a/app-proto/src/add_remove.rs +++ b/app-proto/src/add_remove.rs @@ -173,7 +173,7 @@ pub fn AddRemove() -> View { } ) { "+" } button( - class="emoji", /* KLUDGE */ + class="emoji", /* KLUDGE */ // for convenience, we're using an emoji as a temporary icon for this button disabled={ let state = use_context::(); state.selection.with(|sel| sel.len() != 2) -- 2.34.1 From a48fef364197305e15c5f69708c08547b43d1906 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Thu, 14 Nov 2024 00:55:33 -0800 Subject: [PATCH 22/23] Comment example assembly chooser Also, add a way to load the empty assembly. --- app-proto/src/add_remove.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app-proto/src/add_remove.rs b/app-proto/src/add_remove.rs index 5590b6c..ba02e65 100644 --- a/app-proto/src/add_remove.rs +++ b/app-proto/src/add_remove.rs @@ -4,6 +4,8 @@ use web_sys::{console, wasm_bindgen::JsValue}; use crate::{engine, AppState, assembly::{Assembly, Constraint, Element}}; /* DEBUG */ +// load an example assembly for testing. this code will be removed once we've +// built a more formal test assembly system fn load_gen_assemb(assembly: &Assembly) { let _ = assembly.try_insert_element( Element::new( @@ -56,6 +58,8 @@ fn load_gen_assemb(assembly: &Assembly) { } /* DEBUG */ +// load an example assembly for testing. this code will be removed once we've +// built a more formal test assembly system fn load_low_curv_assemb(assembly: &Assembly) { let a = 0.75_f64.sqrt(); let _ = assembly.try_insert_element( @@ -226,9 +230,10 @@ pub fn AddRemove() -> View { }); } ) { "🔗" } - select(bind:value=assembly_name) { /* DEBUG */ + select(bind:value=assembly_name) { /* DEBUG */ // example assembly chooser option(value="general") { "General" } option(value="low-curv") { "Low-curvature" } + option(value="empty") { "Empty" } } } } -- 2.34.1 From 7d6a394156aa26372ad0d98697fcb0e97a441ea4 Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Thu, 14 Nov 2024 01:18:20 -0800 Subject: [PATCH 23/23] Name element column index more descriptively --- app-proto/src/assembly.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index 9a4dd94..35b4417 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -23,7 +23,7 @@ pub struct Element { // the configuration matrix column index that was assigned to this element // last time the assembly was realized - index: usize + column_index: usize } impl Element { @@ -39,7 +39,7 @@ impl Element { color: color, representation: create_signal(representation), constraints: create_signal(BTreeSet::default()), - index: 0 + column_index: 0 } } } @@ -133,7 +133,7 @@ impl Assembly { // index the elements self.elements.update_silent(|elts| { for (index, (_, elt)) in elts.into_iter().enumerate() { - elt.index = index; + elt.column_index = index; } }); @@ -145,8 +145,8 @@ impl Assembly { for (_, cst) in csts { if cst.active.get_untracked() && cst.lorentz_prod_valid.get_untracked() { let subjects = cst.subjects; - let row = elts[subjects.0].index; - let col = elts[subjects.1].index; + let row = elts[subjects.0].column_index; + let col = elts[subjects.1].column_index; gram_to_be.push_sym(row, col, cst.lorentz_prod.get_untracked()); } } @@ -156,7 +156,7 @@ impl Assembly { // Gram matrix let mut guess_to_be = DMatrix::::zeros(5, elts.len()); for (_, elt) in elts { - let index = elt.index; + let index = elt.column_index; gram_to_be.push_sym(index, index, 1.0); guess_to_be.set_column(index, &elt.representation.get_clone_untracked()); } @@ -202,7 +202,7 @@ impl Assembly { // read out the solution for (_, elt) in self.elements.get_clone_untracked() { elt.representation.update( - |rep| rep.set_column(0, &config.column(elt.index)) + |rep| rep.set_column(0, &config.column(elt.column_index)) ); } } -- 2.34.1