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 ba8a20b..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 */
@@ -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;
}
diff --git a/app-proto/src/add_remove.rs b/app-proto/src/add_remove.rs
index ead66dd..d135449 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],
- rep: engine::sphere(0.5, 0.5, 0.0, 1.0),
- constraints: 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],
- rep: engine::sphere(-0.5, -0.5, 0.0, 1.0),
- constraints: 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],
- rep: engine::sphere(-0.5, 0.5, 0.0, 0.75),
- constraints: 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],
- rep: engine::sphere(0.5, -0.5, 0.0, 0.5),
- constraints: 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],
- rep: engine::sphere(0.0, 0.15, 1.0, 0.25),
- constraints: 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],
- rep: engine::sphere(0.0, -0.15, -1.0, 0.25),
- constraints: 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],
- rep: engine::sphere(0.0, 0.0, 0.0, 1.0),
- constraints: 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],
- rep: engine::sphere_with_offset(0.0, 0.0, 1.0, 0.0, 0.0),
- constraints: 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],
- rep: engine::sphere_with_offset(1.0, 0.0, 0.0, 1.0, 0.0),
- constraints: 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],
- rep: engine::sphere_with_offset(-0.5, a, 0.0, 1.0, 0.0),
- constraints: 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],
- rep: engine::sphere_with_offset(-0.5, -a, 0.0, 1.0, 0.0),
- constraints: 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],
- rep: engine::sphere(-4.0/3.0, 0.0, 0.0, 1.0/3.0),
- constraints: 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],
- rep: engine::sphere(2.0/3.0, -4.0/3.0 * a, 0.0, 1.0/3.0),
- constraints: 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],
- rep: engine::sphere(2.0/3.0, 4.0/3.0 * a, 0.0, 1.0/3.0),
- constraints: 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)
+ )
);
}
@@ -216,15 +187,15 @@ pub fn AddRemove() -> View {
}
);
let rep = create_signal(0.0);
+ let rep_valid = create_signal(false);
let active = create_signal(true);
state.assembly.insert_constraint(Constraint {
args: args,
rep: rep,
rep_text: create_signal(String::new()),
- rep_valid: create_signal(false),
+ rep_valid: rep_valid,
active: active,
});
- state.assembly.realize();
state.selection.update(|sel| sel.clear());
/* DEBUG */
@@ -242,15 +213,14 @@ pub fn AddRemove() -> View {
}
});
- // update the realization when the constraint 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", args.0, args.1)
+ ));
rep.track();
- console::log_2(
- &JsValue::from("Lorentz product updated to"),
- &JsValue::from(rep.get_untracked())
- );
- if active.get() {
+ if active.get() && rep_valid.get() {
state.assembly.realize();
}
});
diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs
index 0970932..7b7c015 100644
--- a/app-proto/src/assembly.rs
+++ b/app-proto/src/assembly.rs
@@ -12,13 +12,32 @@ pub struct Element {
pub id: String,
pub label: String,
pub color: [f32; 3],
- pub rep: DVector,
- pub constraints: BTreeSet,
+ pub rep: Signal>,
+ pub constraints: Signal>,
// internal properties, not reflected in any view
pub index: usize
}
+impl Element {
+ pub fn new(
+ id: String,
+ label: String,
+ color: [f32; 3],
+ rep: DVector
+ ) -> Element {
+ Element {
+ id: id,
+ label: label,
+ color: color,
+ rep: create_signal(rep),
+ constraints: create_signal(BTreeSet::default()),
+ index: 0
+ }
+ }
+}
+
+
#[derive(Clone)]
pub struct Constraint {
pub args: (usize, usize),
@@ -82,24 +101,23 @@ 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],
- rep: DVector::::from_column_slice(&[0.0, 0.0, 0.0, 0.5, -0.5]),
- constraints: 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])
+ )
);
}
pub fn insert_constraint(&self, constraint: Constraint) {
let args = constraint.args;
let key = self.constraints.update(|csts| csts.insert(constraint));
- self.elements.update(|elts| {
- elts[args.0].constraints.insert(key);
- elts[args.1].constraints.insert(key);
- });
+ let arg_constraints = self.elements.with(
+ |elts| (elts[args.0].constraints, elts[args.1].constraints)
+ );
+ arg_constraints.0.update(|csts| csts.insert(key));
+ arg_constraints.1.update(|csts| csts.insert(key));
}
// --- realization ---
@@ -133,7 +151,7 @@ impl Assembly {
for (_, elt) in elts {
let index = elt.index;
gram_to_be.push_sym(index, index, 1.0);
- guess_to_be.set_column(index, &elt.rep);
+ guess_to_be.set_column(index, &elt.rep.get_clone_untracked());
}
(gram_to_be, guess_to_be)
@@ -175,11 +193,11 @@ impl Assembly {
if success {
// read out the solution
- self.elements.update(|elts| {
- for (_, elt) in elts.iter_mut() {
- elt.rep.set_column(0, &config.column(elt.index));
- }
- });
+ for (_, elt) in self.elements.get_clone_untracked() {
+ elt.rep.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 c32b470..ce1655d 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.rep.track();
+ }
+ });
state.selection.track();
scene_changed.set(true);
});
@@ -295,23 +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.rep).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.rep.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;
@@ -320,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 4a2b36a..11cc061 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,
@@ -43,13 +43,9 @@ fn ConstraintOutlineItem(constraint_key: usize, element_key: usize) -> View {
constraint.args.0
};
let other_arg_label = assembly.elements.with(|elts| elts[other_arg].label.clone());
- let class = create_memo(move || {
- if constraint.rep_valid.get() {
- "cst"
- } else {
- "cst invalid"
- }
- });
+ let class = constraint.rep_valid.map(
+ |&rep_valid| if rep_valid { "cst" } else { "cst invalid" }
+ );
view! {
li(class=class.get()) {
input(r#type="checkbox", bind:checked=constraint.active)
@@ -64,19 +60,19 @@ fn ConstraintOutlineItem(constraint_key: usize, element_key: usize) -> View {
#[component(inline_props)]
fn ElementOutlineItem(key: usize, 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.rep.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 rep_components = element.rep.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 details_node = create_node_ref();
view! {
li {
@@ -101,7 +97,7 @@ fn ElementOutlineItem(key: usize, element: assembly::Element) -> View {
}
event.prevent_default();
},
- "ArrowRight" if constrained => {
+ "ArrowRight" if constrained.get() => {
let _ = details_node
.get()
.unchecked_into::()
@@ -144,13 +140,20 @@ fn ElementOutlineItem(key: usize, 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")
}
}
ul(class="constraints") {
Keyed(
- list=element.constraints.into_iter().collect::>(),
+ list=constraint_list,
view=move |cst_key| view! {
ConstraintOutlineItem(
constraint_key=cst_key,
@@ -173,15 +176,16 @@ fn ElementOutlineItem(key: usize, 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(
@@ -192,16 +196,11 @@ pub fn Outline() -> View {
}
) {
Keyed(
- list=elements_sorted,
+ list=element_list,
view=|(key, elt)| view! {
ElementOutlineItem(key=key, element=elt)
},
- key=|(key, elt)| (
- key.clone(),
- elt.id.clone(),
- elt.label.clone(),
- elt.constraints.clone()
- )
+ key=|(key, _)| key.clone()
)
}
}