Compare commits
24 Commits
main
...
engine-int
Author | SHA1 | Date | |
---|---|---|---|
|
5332fda6e4 | ||
|
22a93bee28 | ||
|
5839882ed7 | ||
|
a4ec52a4e7 | ||
abb9d35335 | |||
|
a170492e3d | ||
|
b8ca1139d5 | ||
|
ced001bbfe | ||
|
ed1890bffc | ||
|
da008fd090 | ||
|
933f05661d | ||
|
9c191ae586 | ||
|
9e31037e17 | ||
|
c2e3c64d4a | ||
|
76ad4245d5 | ||
|
a46ef2c8d6 | ||
|
e0880d2ad2 | ||
|
e5f4d523f9 | ||
|
a37c71153d | ||
|
ce33bbf418 | ||
|
9f8632efb3 | ||
|
9fe03264ab | ||
|
e59d60bf77 | ||
|
16df161fe7 |
@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "dyna3"
|
||||
name = "sketch-outline"
|
||||
version = "0.1.0"
|
||||
authors = ["Aaron Fenyes", "Glen Whitney"]
|
||||
authors = ["Aaron"]
|
||||
edition = "2021"
|
||||
|
||||
[features]
|
||||
|
@ -2,10 +2,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>dyna3</title>
|
||||
<title>Sketch outline</title>
|
||||
<link data-trunk rel="css" href="main.css"/>
|
||||
<link href="https://fonts.bunny.net/css?family=fira-sans:ital,wght@0,400;1,400&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.bunny.net/css?family=noto-emoji:wght@400&text=%f0%9f%94%97%e2%9a%a0&display=swap" rel="stylesheet">
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
|
@ -1,20 +1,7 @@
|
||||
: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: var(--text);
|
||||
background-color: var(--page-background);
|
||||
font-family: 'Fira Sans', sans-serif;
|
||||
color: #fcfcfc;
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
/* sidebar */
|
||||
@ -29,7 +16,7 @@ body {
|
||||
padding: 0px;
|
||||
border-width: 0px 1px 0px 0px;
|
||||
border-style: solid;
|
||||
border-color: var(--border);
|
||||
border-color: #555;
|
||||
}
|
||||
|
||||
/* add-remove */
|
||||
@ -46,15 +33,6 @@ body {
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
/* outline */
|
||||
|
||||
#outline {
|
||||
@ -73,103 +51,81 @@ summary {
|
||||
}
|
||||
|
||||
summary.selected {
|
||||
color: var(--text-bright);
|
||||
background-color: var(--selection-highlight);
|
||||
color: #fff;
|
||||
background-color: #444;
|
||||
}
|
||||
|
||||
summary > div, .constraint {
|
||||
summary > div, .cst {
|
||||
padding-top: 4px;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.element, .constraint {
|
||||
.elt, .cst {
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
padding-left: 8px;
|
||||
padding-right: 8px;
|
||||
}
|
||||
|
||||
.element-switch {
|
||||
.elt-switch {
|
||||
width: 18px;
|
||||
padding-left: 2px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
details:has(li) .element-switch::after {
|
||||
details:has(li) .elt-switch::after {
|
||||
content: '▸';
|
||||
}
|
||||
|
||||
details[open]:has(li) .element-switch::after {
|
||||
details[open]:has(li) .elt-switch::after {
|
||||
content: '▾';
|
||||
}
|
||||
|
||||
.element-label {
|
||||
.elt-label {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.constraint-label {
|
||||
.cst-label {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.element-representation {
|
||||
.elt-rep {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.element-representation > div {
|
||||
.elt-rep > div {
|
||||
padding: 2px 0px 0px 0px;
|
||||
font-size: 10pt;
|
||||
font-variant-numeric: tabular-nums;
|
||||
text-align: right;
|
||||
text-align: center;
|
||||
width: 56px;
|
||||
}
|
||||
|
||||
.constraint {
|
||||
.cst {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.constraint.invalid {
|
||||
color: var(--text-invalid);
|
||||
}
|
||||
|
||||
.constraint > input[type=checkbox] {
|
||||
.cst > input[type=checkbox] {
|
||||
margin: 0px 8px 0px 0px;
|
||||
}
|
||||
|
||||
.constraint > input[type=text] {
|
||||
color: inherit;
|
||||
.cst > input[type=text] {
|
||||
color: #fcfcfc;
|
||||
background-color: inherit;
|
||||
border: 1px solid var(--border);
|
||||
border: 1px solid #555;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.constraint.invalid > input[type=text] {
|
||||
border-color: var(--border-invalid);
|
||||
}
|
||||
|
||||
.status {
|
||||
width: 20px;
|
||||
padding-left: 4px;
|
||||
text-align: center;
|
||||
font-family: 'Noto Emoji';
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.invalid > .status::after, details:has(.invalid):not([open]) .status::after {
|
||||
content: '⚠';
|
||||
color: var(--text-invalid);
|
||||
}
|
||||
|
||||
/* display */
|
||||
|
||||
canvas {
|
||||
float: left;
|
||||
margin-left: 20px;
|
||||
margin-top: 20px;
|
||||
background-color: var(--display-background);
|
||||
border: 1px solid var(--border);
|
||||
background-color: #020202;
|
||||
border: 1px solid #555;
|
||||
border-radius: 16px;
|
||||
}
|
||||
|
||||
canvas:focus {
|
||||
border-color: var(--border-focus);
|
||||
border-color: #aaa;
|
||||
}
|
@ -1,130 +1,155 @@
|
||||
use std::collections::BTreeSet; /* DEBUG */
|
||||
use sycamore::prelude::*;
|
||||
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(
|
||||
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
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/* 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(
|
||||
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
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@ -177,7 +202,6 @@ pub fn AddRemove() -> View {
|
||||
}
|
||||
) { "+" }
|
||||
button(
|
||||
class="emoji", /* KLUDGE */ // for convenience, we're using an emoji as a temporary icon for this button
|
||||
disabled={
|
||||
let state = use_context::<AppState>();
|
||||
state.selection.with(|sel| sel.len() != 2)
|
||||
@ -191,15 +215,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 */
|
||||
@ -217,23 +241,23 @@ 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();
|
||||
}
|
||||
});
|
||||
}
|
||||
) { "🔗" }
|
||||
select(bind:value=assembly_name) { /* DEBUG */ // example assembly chooser
|
||||
select(bind:value=assembly_name) { /* DEBUG */
|
||||
option(value="general") { "General" }
|
||||
option(value="low-curv") { "Low-curvature" }
|
||||
option(value="empty") { "Empty" }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -18,33 +18,17 @@ pub struct Element {
|
||||
pub id: String,
|
||||
pub label: String,
|
||||
pub color: ElementColor,
|
||||
pub representation: Signal<DVector<f64>>,
|
||||
pub constraints: Signal<BTreeSet<ConstraintKey>>,
|
||||
pub representation: DVector<f64>,
|
||||
pub constraints: BTreeSet<ConstraintKey>,
|
||||
|
||||
// the configuration matrix column index that was assigned to this element
|
||||
// last time the assembly was realized
|
||||
column_index: usize
|
||||
/* 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
|
||||
}
|
||||
|
||||
impl Element {
|
||||
pub fn new(
|
||||
id: String,
|
||||
label: String,
|
||||
color: ElementColor,
|
||||
representation: DVector<f64>
|
||||
) -> Element {
|
||||
Element {
|
||||
id: id,
|
||||
label: label,
|
||||
color: color,
|
||||
representation: create_signal(representation),
|
||||
constraints: create_signal(BTreeSet::default()),
|
||||
column_index: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Constraint {
|
||||
pub subjects: (ElementKey, ElementKey),
|
||||
@ -108,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::<f64>::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::<f64>::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 ---
|
||||
@ -133,7 +118,7 @@ impl Assembly {
|
||||
// index the elements
|
||||
self.elements.update_silent(|elts| {
|
||||
for (index, (_, elt)) in elts.into_iter().enumerate() {
|
||||
elt.column_index = index;
|
||||
elt.index = index;
|
||||
}
|
||||
});
|
||||
|
||||
@ -145,8 +130,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].column_index;
|
||||
let col = elts[subjects.1].column_index;
|
||||
let row = elts[subjects.0].index;
|
||||
let col = elts[subjects.1].index;
|
||||
gram_to_be.push_sym(row, col, cst.lorentz_prod.get_untracked());
|
||||
}
|
||||
}
|
||||
@ -156,9 +141,9 @@ impl Assembly {
|
||||
// Gram matrix
|
||||
let mut guess_to_be = DMatrix::<f64>::zeros(5, elts.len());
|
||||
for (_, elt) in elts {
|
||||
let index = elt.column_index;
|
||||
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)
|
||||
@ -200,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.column_index))
|
||||
);
|
||||
}
|
||||
self.elements.update(|elts| {
|
||||
for (_, elt) in elts.iter_mut() {
|
||||
elt.representation.set_column(0, &config.column(elt.index));
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -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::<Vec<_>>(),
|
||||
|
||||
// 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::<Vec<_>>(),
|
||||
|
||||
// highlight levels
|
||||
elts.iter().map(|(key, _)| {
|
||||
if state.selection.with(|sel| sel.contains(&key)) {
|
||||
1.0_f32
|
||||
} else {
|
||||
HIGHLIGHT
|
||||
}
|
||||
}).collect::<Vec<_>>()
|
||||
)
|
||||
});
|
||||
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(
|
||||
|
@ -1,6 +1,7 @@
|
||||
use itertools::Itertools;
|
||||
use sycamore::prelude::*;
|
||||
use sycamore::{prelude::*, web::tags::div};
|
||||
use web_sys::{
|
||||
Element,
|
||||
Event,
|
||||
HtmlInputElement,
|
||||
KeyboardEvent,
|
||||
@ -8,7 +9,7 @@ use web_sys::{
|
||||
wasm_bindgen::JsCast
|
||||
};
|
||||
|
||||
use crate::{AppState, assembly, assembly::{Constraint, ConstraintKey, ElementKey}};
|
||||
use crate::{AppState, assembly::Constraint};
|
||||
|
||||
// an editable view of the Lorentz product representing a constraint
|
||||
#[component(inline_props)]
|
||||
@ -31,143 +32,6 @@ 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::<AppState>();
|
||||
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());
|
||||
let class = constraint.lorentz_prod_valid.map(
|
||||
|&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="constraint-label") { (other_subject_label) }
|
||||
LorentzProductInput(constraint=constraint)
|
||||
div(class="status")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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::<AppState>();
|
||||
let class = state.selection.map(
|
||||
move |sel| if 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 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.get() => {
|
||||
let _ = details_node
|
||||
.get()
|
||||
.unchecked_into::<web_sys::Element>()
|
||||
.set_attribute("open", "");
|
||||
},
|
||||
"ArrowLeft" => {
|
||||
let _ = details_node
|
||||
.get()
|
||||
.unchecked_into::<web_sys::Element>()
|
||||
.remove_attribute("open");
|
||||
},
|
||||
_ => ()
|
||||
}
|
||||
}
|
||||
}
|
||||
) {
|
||||
div(
|
||||
class="element-switch",
|
||||
on:click=|event: MouseEvent| event.stop_propagation()
|
||||
)
|
||||
div(
|
||||
class="element",
|
||||
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="element-label") { (label) }
|
||||
div(class="element-representation") {
|
||||
Indexed(
|
||||
list=rep_components,
|
||||
view=|coord_str| view! {
|
||||
div { (coord_str) }
|
||||
}
|
||||
)
|
||||
}
|
||||
div(class="status")
|
||||
}
|
||||
}
|
||||
ul(class="constraints") {
|
||||
Keyed(
|
||||
list=constraint_list,
|
||||
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:
|
||||
@ -176,16 +40,15 @@ fn ElementOutlineItem(key: ElementKey, element: assembly::Element) -> View {
|
||||
//
|
||||
#[component]
|
||||
pub fn Outline() -> View {
|
||||
let state = use_context::<AppState>();
|
||||
|
||||
// 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::<AppState>();
|
||||
state.assembly.elements
|
||||
.get_clone()
|
||||
.into_iter()
|
||||
.sorted_by_key(|(_, elt)| elt.id.clone())
|
||||
.collect()
|
||||
);
|
||||
});
|
||||
|
||||
view! {
|
||||
ul(
|
||||
@ -196,11 +59,128 @@ pub fn Outline() -> View {
|
||||
}
|
||||
) {
|
||||
Keyed(
|
||||
list=element_list,
|
||||
view=|(key, elt)| view! {
|
||||
ElementOutlineItem(key=key, element=elt)
|
||||
list=elements_sorted,
|
||||
view=|(key, elt)| {
|
||||
let state = use_context::<AppState>();
|
||||
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::<Vec<_>>();
|
||||
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::<Element>()
|
||||
.set_attribute("open", "");
|
||||
},
|
||||
"ArrowLeft" => {
|
||||
let _ = details_node
|
||||
.get()
|
||||
.unchecked_into::<Element>()
|
||||
.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::<Vec<_>>(),
|
||||
view=move |c_key| {
|
||||
let c_state = use_context::<AppState>();
|
||||
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)
|
||||
}
|
||||
}
|
||||
},
|
||||
key=|c_key| c_key.clone()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
key=|(key, _)| key.clone()
|
||||
key=|(key, elt)| (
|
||||
key.clone(),
|
||||
elt.id.clone(),
|
||||
elt.label.clone(),
|
||||
elt.constraints.clone()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user