Compare commits
No commits in common. "f5486fb0dd2c4f5f59ea52782af464c2faa4c714" and "96f8b6b5f34c5374b4aebab3cf5d9e582e37ecb3" have entirely different histories.
f5486fb0dd
...
96f8b6b5f3
4
app-proto/full-interface/.gitignore
vendored
4
app-proto/full-interface/.gitignore
vendored
@ -1,4 +0,0 @@
|
|||||||
target
|
|
||||||
dist
|
|
||||||
profiling
|
|
||||||
Cargo.lock
|
|
@ -1,120 +0,0 @@
|
|||||||
body {
|
|
||||||
margin: 0px;
|
|
||||||
color: #fcfcfc;
|
|
||||||
background-color: #222;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* sidebar */
|
|
||||||
|
|
||||||
#sidebar {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
float: left;
|
|
||||||
width: 450px;
|
|
||||||
height: 100vh;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
border-width: 0px 1px 0px 0px;
|
|
||||||
border-style: solid;
|
|
||||||
border-color: #555;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* add-remove */
|
|
||||||
|
|
||||||
#add-remove {
|
|
||||||
display: flex;
|
|
||||||
gap: 8px;
|
|
||||||
margin: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#add-remove > button {
|
|
||||||
width: 32px;
|
|
||||||
height: 32px;
|
|
||||||
font-size: large;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* outline */
|
|
||||||
|
|
||||||
#outline {
|
|
||||||
flex-grow: 1;
|
|
||||||
margin: 0px;
|
|
||||||
padding: 0px;
|
|
||||||
overflow-y: scroll;
|
|
||||||
}
|
|
||||||
|
|
||||||
li {
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
summary {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
summary.selected {
|
|
||||||
color: #fff;
|
|
||||||
background-color: #444;
|
|
||||||
}
|
|
||||||
|
|
||||||
summary > div, .cst {
|
|
||||||
padding-top: 4px;
|
|
||||||
padding-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.elt, .cst {
|
|
||||||
display: flex;
|
|
||||||
flex-grow: 1;
|
|
||||||
padding-left: 8px;
|
|
||||||
padding-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.elt-switch {
|
|
||||||
width: 18px;
|
|
||||||
padding-left: 2px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
details:has(li) .elt-switch::after {
|
|
||||||
content: '▸';
|
|
||||||
}
|
|
||||||
|
|
||||||
details[open]:has(li) .elt-switch::after {
|
|
||||||
content: '▾';
|
|
||||||
}
|
|
||||||
|
|
||||||
.elt-label {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cst-label {
|
|
||||||
flex-grow: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.elt-rep {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.elt-rep > div, .cst-rep {
|
|
||||||
padding: 2px 0px 0px 0px;
|
|
||||||
font-size: 10pt;
|
|
||||||
text-align: center;
|
|
||||||
width: 56px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cst {
|
|
||||||
font-style: italic;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* display */
|
|
||||||
|
|
||||||
canvas {
|
|
||||||
float: left;
|
|
||||||
margin-left: 20px;
|
|
||||||
margin-top: 20px;
|
|
||||||
background-color: #020202;
|
|
||||||
border: 1px solid #555;
|
|
||||||
border-radius: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
canvas:focus {
|
|
||||||
border-color: #aaa;
|
|
||||||
}
|
|
@ -1,55 +0,0 @@
|
|||||||
use sycamore::prelude::*;
|
|
||||||
use web_sys::{MouseEvent, console, wasm_bindgen::JsValue};
|
|
||||||
|
|
||||||
use crate::AppState;
|
|
||||||
use crate::Constraint;
|
|
||||||
|
|
||||||
#[component]
|
|
||||||
pub fn AddRemove() -> View {
|
|
||||||
let state = use_context::<AppState>();
|
|
||||||
|
|
||||||
view! {
|
|
||||||
div(id="add-remove") {
|
|
||||||
button(
|
|
||||||
on:click=move |event: MouseEvent| {
|
|
||||||
console::log_1(&JsValue::from("constraints:"));
|
|
||||||
state.assembly.constraints.with(|csts| {
|
|
||||||
for (_, cst) in csts.into_iter() {
|
|
||||||
console::log_4(
|
|
||||||
&JsValue::from(cst.args.0),
|
|
||||||
&JsValue::from(cst.args.1),
|
|
||||||
&JsValue::from(":"),
|
|
||||||
&JsValue::from(cst.rep)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
) { "+" }
|
|
||||||
button(
|
|
||||||
disabled={
|
|
||||||
state.selection.with(|sel| sel.len() != 2)
|
|
||||||
},
|
|
||||||
on:click=move |event: MouseEvent| {
|
|
||||||
let args = state.selection.with(
|
|
||||||
|sel| {
|
|
||||||
let arg_vec: Vec<_> = sel.into_iter().collect();
|
|
||||||
(arg_vec[0].clone(), arg_vec[1].clone())
|
|
||||||
}
|
|
||||||
);
|
|
||||||
console::log_5(
|
|
||||||
&JsValue::from("add constraint"),
|
|
||||||
&JsValue::from(args.0),
|
|
||||||
&JsValue::from(args.1),
|
|
||||||
&JsValue::from(":"),
|
|
||||||
&JsValue::from(0.0)
|
|
||||||
);
|
|
||||||
state.assembly.insert_constraint(Constraint {
|
|
||||||
args: args,
|
|
||||||
rep: 0.0
|
|
||||||
});
|
|
||||||
state.selection.update(|sel| sel.clear());
|
|
||||||
}
|
|
||||||
) { "🔗" }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
use nalgebra::DVector;
|
|
||||||
use rustc_hash::FxHashSet;
|
|
||||||
use slab::Slab;
|
|
||||||
use sycamore::prelude::*;
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
|
||||||
pub struct Element {
|
|
||||||
pub id: String,
|
|
||||||
pub label: String,
|
|
||||||
pub color: [f32; 3],
|
|
||||||
pub rep: DVector<f64>,
|
|
||||||
pub constraints: FxHashSet<usize>
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Constraint {
|
|
||||||
pub args: (usize, usize),
|
|
||||||
pub rep: f64
|
|
||||||
}
|
|
||||||
|
|
||||||
// a complete, view-independent description of an assembly
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct Assembly {
|
|
||||||
pub elements: Signal<Slab<Element>>,
|
|
||||||
pub constraints: Signal<Slab<Constraint>>
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Assembly {
|
|
||||||
pub fn new() -> Assembly {
|
|
||||||
Assembly {
|
|
||||||
elements: create_signal(Slab::new()),
|
|
||||||
constraints: create_signal(Slab::new())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,155 +0,0 @@
|
|||||||
use itertools::Itertools;
|
|
||||||
use sycamore::{prelude::*, web::tags::div};
|
|
||||||
use web_sys::{Element, KeyboardEvent, MouseEvent, wasm_bindgen::JsCast};
|
|
||||||
|
|
||||||
use crate::AppState;
|
|
||||||
|
|
||||||
// this component lists the elements of the assembly, showing the constraints
|
|
||||||
// on each element as a collapsible sub-list. its implementation is based on
|
|
||||||
// Kate Morley's HTML + CSS tree views:
|
|
||||||
//
|
|
||||||
// https://iamkate.com/code/tree-views/
|
|
||||||
//
|
|
||||||
#[component]
|
|
||||||
pub fn Outline() -> View {
|
|
||||||
// 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(
|
|
||||||
id="outline",
|
|
||||||
on:click={
|
|
||||||
let state = use_context::<AppState>();
|
|
||||||
move |_| state.selection.update(|sel| sel.clear())
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
Keyed(
|
|
||||||
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.rep.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! {
|
|
||||||
/* [TO DO] switch to integer-valued parameters whenever
|
|
||||||
that becomes possible again */
|
|
||||||
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: usize| {
|
|
||||||
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.args.0 == key {
|
|
||||||
cst.args.1
|
|
||||||
} else {
|
|
||||||
cst.args.0
|
|
||||||
};
|
|
||||||
let other_arg_label = assembly.elements.with(|elts| elts[other_arg].label.clone());
|
|
||||||
view! {
|
|
||||||
li(class="cst") {
|
|
||||||
div(class="cst-label") { (other_arg_label) }
|
|
||||||
div(class="cst-rep") { (cst.rep) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
key=|c_key| c_key.clone()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
key=|(key, _)| key.clone()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
1
app-proto/inversive-display/.gitignore
vendored
1
app-proto/inversive-display/.gitignore
vendored
@ -1,4 +1,3 @@
|
|||||||
target
|
target
|
||||||
dist
|
dist
|
||||||
profiling
|
|
||||||
Cargo.lock
|
Cargo.lock
|
3
app-proto/sketch-outline/.gitignore
vendored
Normal file
3
app-proto/sketch-outline/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
target
|
||||||
|
dist
|
||||||
|
Cargo.lock
|
@ -12,7 +12,6 @@ itertools = "0.13.0"
|
|||||||
js-sys = "0.3.70"
|
js-sys = "0.3.70"
|
||||||
nalgebra = "0.33.0"
|
nalgebra = "0.33.0"
|
||||||
rustc-hash = "2.0.0"
|
rustc-hash = "2.0.0"
|
||||||
slab = "0.4.9"
|
|
||||||
sycamore = "0.9.0-beta.3"
|
sycamore = "0.9.0-beta.3"
|
||||||
|
|
||||||
# The `console_error_panic_hook` crate provides better debugging of panics by
|
# The `console_error_panic_hook` crate provides better debugging of panics by
|
79
app-proto/sketch-outline/main.css
Normal file
79
app-proto/sketch-outline/main.css
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
body {
|
||||||
|
margin-left: 20px;
|
||||||
|
margin-top: 20px;
|
||||||
|
color: #fcfcfc;
|
||||||
|
background-color: #222;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* outline */
|
||||||
|
|
||||||
|
ul {
|
||||||
|
float: left;
|
||||||
|
width: 450px;
|
||||||
|
height: 750px;
|
||||||
|
margin: 0px;
|
||||||
|
padding: 8px;
|
||||||
|
border: 1px solid #555;
|
||||||
|
border-radius: 16px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: flex;
|
||||||
|
padding: 3px;
|
||||||
|
list-style-type: none;
|
||||||
|
background-color: #444;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.selected {
|
||||||
|
color: #fff;
|
||||||
|
background-color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
li:not(:last-child) {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li > .elt-label {
|
||||||
|
flex-grow: 1;
|
||||||
|
padding: 2px 0px 2px 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li > .elt-rep {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
li > .elt-rep > div {
|
||||||
|
padding: 2px;
|
||||||
|
margin-left: 3px;
|
||||||
|
text-align: center;
|
||||||
|
width: 60px;
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
li.selected > .elt-rep > div {
|
||||||
|
background-color: #555;
|
||||||
|
}
|
||||||
|
|
||||||
|
li > .elt-rep > div:first-child {
|
||||||
|
border-radius: 6px 0px 0px 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
li > .elt-rep > div:last-child {
|
||||||
|
border-radius: 0px 6px 6px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* display */
|
||||||
|
|
||||||
|
canvas {
|
||||||
|
float: left;
|
||||||
|
margin-left: 16px;
|
||||||
|
background-color: #020202;
|
||||||
|
border: 1px solid #555;
|
||||||
|
border-radius: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas:focus {
|
||||||
|
border-color: #aaa;
|
||||||
|
}
|
18
app-proto/sketch-outline/src/assembly.rs
Normal file
18
app-proto/sketch-outline/src/assembly.rs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
use nalgebra::DVector;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
use sycamore::reactive::Signal;
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq)]
|
||||||
|
pub struct Element {
|
||||||
|
pub id: String,
|
||||||
|
pub label: String,
|
||||||
|
pub color: [f32; 3],
|
||||||
|
pub rep: DVector<f64>
|
||||||
|
}
|
||||||
|
|
||||||
|
// a complete, view-independent description of an assembly
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct Assembly {
|
||||||
|
// the order of the elements is arbitrary, and it could change at any time
|
||||||
|
pub elements: Signal<FxHashMap<String, Element>>
|
||||||
|
}
|
@ -288,17 +288,17 @@ pub fn Display() -> View {
|
|||||||
|
|
||||||
// get the assembly
|
// get the assembly
|
||||||
let elements = state.assembly.elements.get_clone();
|
let elements = state.assembly.elements.get_clone();
|
||||||
let element_iter = (&elements).into_iter();
|
let element_iter = (&elements).values();
|
||||||
let reps_world: Vec<_> = element_iter.clone().map(|(_, elt)| &assembly_to_world * &elt.rep).collect();
|
let reps_world: Vec<_> = element_iter.clone().map(|elt| &assembly_to_world * &elt.rep).collect();
|
||||||
let colors: Vec<_> = element_iter.clone().map(|(key, elt)|
|
let colors: Vec<_> = element_iter.clone().map(|elt|
|
||||||
if state.selection.with(|sel| sel.contains(&key)) {
|
if state.selection.with(|sel| sel.contains(&elt.id)) {
|
||||||
elt.color.map(|ch| 0.2 + 0.8*ch)
|
elt.color.map(|ch| 0.2 + 0.8*ch)
|
||||||
} else {
|
} else {
|
||||||
elt.color
|
elt.color
|
||||||
}
|
}
|
||||||
).collect();
|
).collect();
|
||||||
let highlights: Vec<_> = element_iter.map(|(key, _)|
|
let highlights: Vec<_> = element_iter.map(|elt|
|
||||||
if state.selection.with(|sel| sel.contains(&key)) {
|
if state.selection.with(|sel| sel.contains(&elt.id)) {
|
||||||
1.0_f32
|
1.0_f32
|
||||||
} else {
|
} else {
|
||||||
HIGHLIGHT
|
HIGHLIGHT
|
||||||
@ -386,8 +386,8 @@ pub fn Display() -> View {
|
|||||||
// again
|
// again
|
||||||
canvas(
|
canvas(
|
||||||
ref=display,
|
ref=display,
|
||||||
width="600",
|
width="750",
|
||||||
height="600",
|
height="750",
|
||||||
tabindex="0",
|
tabindex="0",
|
||||||
on:keydown=move |event: KeyboardEvent| {
|
on:keydown=move |event: KeyboardEvent| {
|
||||||
if event.key() == "Shift" {
|
if event.key() == "Shift" {
|
@ -1,79 +1,66 @@
|
|||||||
mod add_remove;
|
|
||||||
mod assembly;
|
mod assembly;
|
||||||
mod display;
|
mod display;
|
||||||
mod outline;
|
mod outline;
|
||||||
|
|
||||||
use nalgebra::DVector;
|
use nalgebra::DVector;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use sycamore::prelude::*;
|
use sycamore::prelude::*;
|
||||||
|
|
||||||
use add_remove::AddRemove;
|
use assembly::{Assembly, Element};
|
||||||
use assembly::{Assembly, Constraint, Element};
|
|
||||||
use display::Display;
|
use display::Display;
|
||||||
use outline::Outline;
|
use outline::Outline;
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
struct AppState {
|
struct AppState {
|
||||||
assembly: Assembly,
|
assembly: Assembly,
|
||||||
selection: Signal<FxHashSet<usize>>
|
selection: Signal<FxHashSet<String>>
|
||||||
}
|
|
||||||
|
|
||||||
impl AppState {
|
|
||||||
fn new() -> AppState {
|
|
||||||
AppState {
|
|
||||||
assembly: Assembly::new(),
|
|
||||||
selection: create_signal(FxHashSet::default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
sycamore::render(|| {
|
sycamore::render(|| {
|
||||||
let state = AppState::new();
|
let state = AppState {
|
||||||
let key_a = state.assembly.elements.update(
|
assembly: Assembly {
|
||||||
|
elements: create_signal(FxHashMap::default())
|
||||||
|
},
|
||||||
|
selection: create_signal(FxHashSet::default())
|
||||||
|
};
|
||||||
|
state.assembly.elements.update(
|
||||||
|elts| elts.insert(
|
|elts| elts.insert(
|
||||||
|
"wing_a".to_string(),
|
||||||
Element {
|
Element {
|
||||||
id: String::from("wing_a"),
|
id: String::from("wing_a"),
|
||||||
label: String::from("Wing A"),
|
label: String::from("Wing A"),
|
||||||
color: [1.00_f32, 0.25_f32, 0.00_f32],
|
color: [1.00_f32, 0.25_f32, 0.00_f32],
|
||||||
rep: DVector::<f64>::from_column_slice(&[0.5, 0.5, 0.0, 0.5, -0.25]),
|
rep: DVector::<f64>::from_column_slice(&[0.5, 0.5, 0.0, 0.5, -0.25])
|
||||||
constraints: FxHashSet::default()
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
let key_b = state.assembly.elements.update(
|
state.assembly.elements.update(
|
||||||
|elts| elts.insert(
|
|elts| elts.insert(
|
||||||
|
"wing_b".to_string(),
|
||||||
Element {
|
Element {
|
||||||
id: String::from("wing_b"),
|
id: String::from("wing_b"),
|
||||||
label: String::from("Wing B"),
|
label: String::from("Wing B"),
|
||||||
color: [0.00_f32, 0.25_f32, 1.00_f32],
|
color: [0.00_f32, 0.25_f32, 1.00_f32],
|
||||||
rep: DVector::<f64>::from_column_slice(&[-0.5, -0.5, 0.0, 0.5, -0.25]),
|
rep: DVector::<f64>::from_column_slice(&[-0.5, -0.5, 0.0, 0.5, -0.25])
|
||||||
constraints: FxHashSet::default()
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
state.assembly.elements.update(
|
state.assembly.elements.update(
|
||||||
|elts| elts.insert(
|
|elts| elts.insert(
|
||||||
|
"central".to_string(),
|
||||||
Element {
|
Element {
|
||||||
id: String::from("central"),
|
id: String::from("central"),
|
||||||
label: String::from("Central"),
|
label: String::from("Central"),
|
||||||
color: [0.75_f32, 0.75_f32, 0.75_f32],
|
color: [0.75_f32, 0.75_f32, 0.75_f32],
|
||||||
rep: DVector::<f64>::from_column_slice(&[0.0, 0.0, 0.0, 0.4, -0.625]),
|
rep: DVector::<f64>::from_column_slice(&[0.0, 0.0, 0.0, 0.4, -0.625])
|
||||||
constraints: FxHashSet::default()
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
state.assembly.insert_constraint(Constraint {
|
|
||||||
args: (key_a, key_b),
|
|
||||||
rep: 0.5
|
|
||||||
});
|
|
||||||
provide_context(state);
|
provide_context(state);
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
div(id="sidebar") {
|
|
||||||
AddRemove {}
|
|
||||||
Outline {}
|
Outline {}
|
||||||
}
|
|
||||||
Display {}
|
Display {}
|
||||||
}
|
}
|
||||||
});
|
});
|
80
app-proto/sketch-outline/src/outline.rs
Normal file
80
app-proto/sketch-outline/src/outline.rs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
use itertools::Itertools;
|
||||||
|
use sycamore::{prelude::*, web::tags::div};
|
||||||
|
use web_sys::KeyboardEvent;
|
||||||
|
|
||||||
|
use crate::AppState;
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn Outline() -> View {
|
||||||
|
// 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(|(id, _)| id.clone())
|
||||||
|
.map(|(_, elt)| elt)
|
||||||
|
.collect()
|
||||||
|
});
|
||||||
|
|
||||||
|
view! {
|
||||||
|
ul {
|
||||||
|
Keyed(
|
||||||
|
list=elements_sorted,
|
||||||
|
view=|elt| {
|
||||||
|
let state = use_context::<AppState>();
|
||||||
|
let class = create_memo({
|
||||||
|
let id = elt.id.clone();
|
||||||
|
move || {
|
||||||
|
if state.selection.with(|sel| sel.contains(&id)) {
|
||||||
|
"selected"
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
let label = elt.label.clone();
|
||||||
|
let rep_components = elt.rep.iter().map(|u| {
|
||||||
|
let u_coord = u.to_string().replace("-", "\u{2212}");
|
||||||
|
View::from(div().children(u_coord))
|
||||||
|
}).collect::<Vec<_>>();
|
||||||
|
view! {
|
||||||
|
/* [TO DO] switch to integer-valued parameters whenever
|
||||||
|
that becomes possible again */
|
||||||
|
li(
|
||||||
|
class=class.get(),
|
||||||
|
tabindex="0",
|
||||||
|
on:click={
|
||||||
|
let id = elt.id.clone();
|
||||||
|
move |_| {
|
||||||
|
state.selection.update(|sel| {
|
||||||
|
if !sel.remove(&id) {
|
||||||
|
sel.insert(id.clone());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
on:keydown={
|
||||||
|
let id = elt.id.clone();
|
||||||
|
move |event: KeyboardEvent| {
|
||||||
|
if event.key() == "Enter" {
|
||||||
|
state.selection.update(|sel| {
|
||||||
|
if !sel.remove(&id) {
|
||||||
|
sel.insert(id.clone());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
event.prevent_default();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
div(class="elt-label") { (label) }
|
||||||
|
div(class="elt-rep") { (rep_components) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
key=|elt| elt.id.clone()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user