From fb8e391587f3e3ffde9e7c7d68940fd24dc80a6f Mon Sep 17 00:00:00 2001 From: Aaron Fenyes Date: Sat, 25 Jan 2025 13:00:18 -0800 Subject: [PATCH] Generalize constraints to observables --- app-proto/main.css | 8 +++---- app-proto/src/add_remove.rs | 44 ++++++++++++++++++++++++++----------- app-proto/src/assembly.rs | 18 ++++++++++----- app-proto/src/outline.rs | 42 +++++++++++++++++++++++++---------- 4 files changed, 77 insertions(+), 35 deletions(-) diff --git a/app-proto/main.css b/app-proto/main.css index b9fc0a1..9c45c29 100644 --- a/app-proto/main.css +++ b/app-proto/main.css @@ -131,10 +131,6 @@ details[open]:has(li) .element-switch::after { color: var(--text-invalid); } -.constraint > input[type=checkbox] { - margin: 0px 8px 0px 0px; -} - .constraint > input[type=text] { color: inherit; background-color: inherit; @@ -154,6 +150,10 @@ details[open]:has(li) .element-switch::after { font-style: normal; } +.constrained > .status::after, details:has(.constrained):not([open]) .status::after { + content: '🔗'; +} + .invalid > .status::after, details:has(.invalid):not([open]) .status::after { content: '⚠'; color: var(--text-invalid); diff --git a/app-proto/src/add_remove.rs b/app-proto/src/add_remove.rs index ba02e65..360a12d 100644 --- a/app-proto/src/add_remove.rs +++ b/app-proto/src/add_remove.rs @@ -1,7 +1,17 @@ use sycamore::prelude::*; use web_sys::{console, wasm_bindgen::JsValue}; -use crate::{engine, AppState, assembly::{Assembly, Constraint, Element}}; +use crate::{ + engine, + AppState, + assembly::{ + Assembly, + Constraint, + ConstraintRole, + Element + }, + engine::Q +}; /* DEBUG */ // load an example assembly for testing. this code will be removed once we've @@ -190,15 +200,23 @@ pub fn AddRemove() -> View { (subject_vec[0].clone(), subject_vec[1].clone()) } ); - let lorentz_prod = create_signal(0.0); - let lorentz_prod_valid = create_signal(false); - let active = create_signal(true); + let measured = state.assembly.elements.map( + move |elts| { + let reps = ( + elts[subjects.0].representation.get_clone(), + elts[subjects.1].representation.get_clone() + ); + reps.0.dot(&(&*Q * reps.1)) + } + ); + let desired = create_signal(0.0); + let role = create_signal(ConstraintRole::Measure); state.assembly.insert_constraint(Constraint { subjects: subjects, - lorentz_prod: lorentz_prod, - lorentz_prod_text: create_signal(String::new()), - lorentz_prod_valid: lorentz_prod_valid, - active: active, + measured: measured, + desired: desired, + desired_text: create_signal(String::new()), + role: role, }); state.selection.update(|sel| sel.clear()); @@ -212,19 +230,19 @@ pub fn AddRemove() -> View { &JsValue::from(cst.subjects.0), &JsValue::from(cst.subjects.1), &JsValue::from(":"), - &JsValue::from(cst.lorentz_prod.get_untracked()) + &JsValue::from(cst.desired.get_untracked()) ); } }); - // update the realization when the constraint becomes active - // and valid, or is edited while active and valid + // update the realization when the observable becomes + // constrained, or is edited while constrained 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() { + desired.track(); + if role.with(|r| matches!(r, ConstraintRole::Constrain)) { state.assembly.realize(); } }); diff --git a/app-proto/src/assembly.rs b/app-proto/src/assembly.rs index 7073c9e..37bd484 100644 --- a/app-proto/src/assembly.rs +++ b/app-proto/src/assembly.rs @@ -111,13 +111,19 @@ impl Element { } } +pub enum ConstraintRole { + Measure, + Constrain, + Invalid +} + #[derive(Clone)] pub struct Constraint { pub subjects: (ElementKey, ElementKey), - pub lorentz_prod: Signal, - pub lorentz_prod_text: Signal, - pub lorentz_prod_valid: Signal, - pub active: Signal + pub measured: ReadSignal, + pub desired: Signal, + pub desired_text: Signal, + pub role: Signal } // the velocity is expressed in uniform coordinates @@ -230,11 +236,11 @@ impl Assembly { let mut gram_to_be = PartialMatrix::new(); self.constraints.with_untracked(|csts| { for (_, cst) in csts { - if cst.active.get_untracked() && cst.lorentz_prod_valid.get_untracked() { + if cst.role.with_untracked(|role| matches!(role, ConstraintRole::Constrain)) { let subjects = cst.subjects; let row = elts[subjects.0].column_index.unwrap(); let col = elts[subjects.1].column_index.unwrap(); - gram_to_be.push_sym(row, col, cst.lorentz_prod.get_untracked()); + gram_to_be.push_sym(row, col, cst.desired.get_untracked()); } } }); diff --git a/app-proto/src/outline.rs b/app-proto/src/outline.rs index a6e968d..8459262 100644 --- a/app-proto/src/outline.rs +++ b/app-proto/src/outline.rs @@ -8,7 +8,16 @@ use web_sys::{ wasm_bindgen::JsCast }; -use crate::{AppState, assembly, assembly::{Constraint, ConstraintKey, ElementKey}}; +use crate::{ + AppState, + assembly, + assembly::{ + Constraint, + ConstraintKey, + ConstraintRole::*, + ElementKey + } +}; // an editable view of the Lorentz product representing a constraint #[component(inline_props)] @@ -16,16 +25,22 @@ fn LorentzProductInput(constraint: Constraint) -> View { view! { input( r#type="text", - bind:value=constraint.lorentz_prod_text, + placeholder=constraint.measured.with(|result| result.to_string()), + bind:value=constraint.desired_text, on:change=move |event: Event| { let target: HtmlInputElement = event.target().unwrap().unchecked_into(); - match target.value().parse::() { - Ok(lorentz_prod) => batch(|| { - constraint.lorentz_prod.set(lorentz_prod); - constraint.lorentz_prod_valid.set(true); - }), - Err(_) => constraint.lorentz_prod_valid.set(false) - }; + let value = target.value(); + if value.is_empty() { + constraint.role.set(Measure); + } else { + match target.value().parse::() { + Ok(desired) => batch(|| { + constraint.desired.set(desired); + constraint.role.set(Constrain); + }), + Err(_) => constraint.role.set(Invalid) + }; + } } ) } @@ -43,12 +58,15 @@ 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 { "constraint" } else { "constraint invalid" } + let class = constraint.role.map( + |role| match role { + Measure => "constraint", + Constrain => "constraint constrained", + Invalid => "constraint invalid" + } ); view! { li(class=class.get()) { - input(r#type="checkbox", bind:checked=constraint.active) div(class="constraint-label") { (other_subject_label) } LorentzProductInput(constraint=constraint) div(class="status")