Compare commits
No commits in common. "18ebf3be2c8a9caf0cdec3338bd6ecb1333a144e" and "f5486fb0dd2c4f5f59ea52782af464c2faa4c714" have entirely different histories.
18ebf3be2c
...
f5486fb0dd
@ -1,227 +1,21 @@
|
||||
use std::collections::BTreeSet; /* DEBUG */
|
||||
use sycamore::prelude::*;
|
||||
use web_sys::{console, wasm_bindgen::JsValue};
|
||||
use web_sys::{MouseEvent, console, wasm_bindgen::JsValue};
|
||||
|
||||
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()
|
||||
}
|
||||
);
|
||||
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()
|
||||
}
|
||||
);
|
||||
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()
|
||||
}
|
||||
);
|
||||
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()
|
||||
}
|
||||
);
|
||||
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()
|
||||
}
|
||||
);
|
||||
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()
|
||||
}
|
||||
);
|
||||
assembly.insert_constraint(
|
||||
Constraint {
|
||||
args: (
|
||||
assembly.elements_by_id.with_untracked(|elts_by_id| elts_by_id["gemini_a"]),
|
||||
assembly.elements_by_id.with_untracked(|elts_by_id| elts_by_id["gemini_b"])
|
||||
),
|
||||
rep: 0.5
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/* DEBUG */
|
||||
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()
|
||||
}
|
||||
);
|
||||
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()
|
||||
}
|
||||
);
|
||||
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()
|
||||
}
|
||||
);
|
||||
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()
|
||||
}
|
||||
);
|
||||
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()
|
||||
}
|
||||
);
|
||||
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()
|
||||
}
|
||||
);
|
||||
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()
|
||||
}
|
||||
);
|
||||
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()
|
||||
}
|
||||
);
|
||||
}
|
||||
use crate::AppState;
|
||||
use crate::Constraint;
|
||||
|
||||
#[component]
|
||||
pub fn AddRemove() -> View {
|
||||
/* DEBUG */
|
||||
let assembly_name = create_signal("general".to_string());
|
||||
create_effect(move || {
|
||||
// get name of chosen assembly
|
||||
let name = assembly_name.get_clone();
|
||||
console::log_1(
|
||||
&JsValue::from(format!("Showing assembly \"{}\"", name.clone()))
|
||||
);
|
||||
|
||||
batch(|| {
|
||||
let state = use_context::<AppState>();
|
||||
let assembly = &state.assembly;
|
||||
|
||||
// clear state
|
||||
assembly.elements.update(|elts| elts.clear());
|
||||
assembly.elements_by_id.update(|elts_by_id| elts_by_id.clear());
|
||||
state.selection.update(|sel| sel.clear());
|
||||
|
||||
// load assembly
|
||||
match name.as_str() {
|
||||
"general" => load_gen_assemb(assembly),
|
||||
"low-curv" => load_low_curv_assemb(assembly),
|
||||
_ => ()
|
||||
};
|
||||
});
|
||||
});
|
||||
let state = use_context::<AppState>();
|
||||
|
||||
view! {
|
||||
div(id="add-remove") {
|
||||
button(
|
||||
on:click=|_| {
|
||||
let state = use_context::<AppState>();
|
||||
state.assembly.insert_new_element();
|
||||
|
||||
/* DEBUG */
|
||||
// print updated list of elements by identifier
|
||||
console::log_1(&JsValue::from("elements by identifier:"));
|
||||
for (id, key) in state.assembly.elements_by_id.get_clone().iter() {
|
||||
console::log_3(
|
||||
&JsValue::from(" "),
|
||||
&JsValue::from(id),
|
||||
&JsValue::from(*key)
|
||||
);
|
||||
}
|
||||
}
|
||||
) { "+" }
|
||||
button(
|
||||
disabled={
|
||||
let state = use_context::<AppState>();
|
||||
state.selection.with(|sel| sel.len() != 2)
|
||||
},
|
||||
on:click=|_| {
|
||||
let state = use_context::<AppState>();
|
||||
let args = state.selection.with(
|
||||
|sel| {
|
||||
let arg_vec: Vec<_> = sel.into_iter().collect();
|
||||
(arg_vec[0].clone(), arg_vec[1].clone())
|
||||
}
|
||||
);
|
||||
state.assembly.insert_constraint(Constraint {
|
||||
args: args,
|
||||
rep: 0.0
|
||||
});
|
||||
state.selection.update(|sel| sel.clear());
|
||||
|
||||
/* DEBUG */
|
||||
// print updated constraint list
|
||||
on:click=move |event: MouseEvent| {
|
||||
console::log_1(&JsValue::from("constraints:"));
|
||||
state.assembly.constraints.with(|csts| {
|
||||
for (_, cst) in csts.into_iter() {
|
||||
console::log_5(
|
||||
&JsValue::from(" "),
|
||||
console::log_4(
|
||||
&JsValue::from(cst.args.0),
|
||||
&JsValue::from(cst.args.1),
|
||||
&JsValue::from(":"),
|
||||
@ -230,11 +24,32 @@ pub fn AddRemove() -> View {
|
||||
}
|
||||
});
|
||||
}
|
||||
) { "+" }
|
||||
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());
|
||||
}
|
||||
) { "🔗" }
|
||||
select(bind:value=assembly_name) { /* DEBUG */
|
||||
option(value="general") { "General" }
|
||||
option(value="low-curv") { "Low-curvature" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,6 @@
|
||||
use nalgebra::DVector;
|
||||
use rustc_hash::FxHashMap;
|
||||
use rustc_hash::FxHashSet;
|
||||
use slab::Slab;
|
||||
use std::collections::BTreeSet;
|
||||
use sycamore::prelude::*;
|
||||
|
||||
#[derive(Clone, PartialEq)]
|
||||
@ -10,7 +9,7 @@ pub struct Element {
|
||||
pub label: String,
|
||||
pub color: [f32; 3],
|
||||
pub rep: DVector<f64>,
|
||||
pub constraints: BTreeSet<usize>
|
||||
pub constraints: FxHashSet<usize>
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
@ -22,65 +21,18 @@ pub struct Constraint {
|
||||
// a complete, view-independent description of an assembly
|
||||
#[derive(Clone)]
|
||||
pub struct Assembly {
|
||||
// elements and constraints
|
||||
pub elements: Signal<Slab<Element>>,
|
||||
pub constraints: Signal<Slab<Constraint>>,
|
||||
|
||||
// indexing
|
||||
pub elements_by_id: Signal<FxHashMap<String, usize>>
|
||||
pub constraints: Signal<Slab<Constraint>>
|
||||
}
|
||||
|
||||
impl Assembly {
|
||||
pub fn new() -> Assembly {
|
||||
Assembly {
|
||||
elements: create_signal(Slab::new()),
|
||||
constraints: create_signal(Slab::new()),
|
||||
elements_by_id: create_signal(FxHashMap::default())
|
||||
constraints: create_signal(Slab::new())
|
||||
}
|
||||
}
|
||||
|
||||
// insert an element into the assembly without checking whether we already
|
||||
// have an element with the same identifier. any element that does have the
|
||||
// same identifier will get kicked out of the `elements_by_id` index
|
||||
fn insert_element_unchecked(&self, elt: Element) {
|
||||
let id = elt.id.clone();
|
||||
let key = self.elements.update(|elts| elts.insert(elt));
|
||||
self.elements_by_id.update(|elts_by_id| elts_by_id.insert(id, key));
|
||||
}
|
||||
|
||||
pub fn try_insert_element(&self, elt: Element) -> bool {
|
||||
let can_insert = self.elements_by_id.with_untracked(
|
||||
|elts_by_id| !elts_by_id.contains_key(&elt.id)
|
||||
);
|
||||
if can_insert {
|
||||
self.insert_element_unchecked(elt);
|
||||
}
|
||||
can_insert
|
||||
}
|
||||
|
||||
pub fn insert_new_element(&self) {
|
||||
// find the next unused identifier in the default sequence
|
||||
let mut id_num = 1;
|
||||
let mut id = format!("sphere{}", id_num);
|
||||
while self.elements_by_id.with_untracked(
|
||||
|elts_by_id| elts_by_id.contains_key(&id)
|
||||
) {
|
||||
id_num += 1;
|
||||
id = format!("sphere{}", id_num);
|
||||
}
|
||||
|
||||
// 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::<f64>::from_column_slice(&[0.0, 0.0, 0.0, 0.5, -0.5]),
|
||||
constraints: BTreeSet::default()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
pub fn insert_constraint(&self, constraint: Constraint) {
|
||||
let args = constraint.args;
|
||||
let key = self.constraints.update(|csts| csts.insert(constraint));
|
||||
|
@ -98,12 +98,10 @@ pub fn Display() -> View {
|
||||
let roll_cw = create_signal(0.0);
|
||||
let zoom_in = create_signal(0.0);
|
||||
let zoom_out = create_signal(0.0);
|
||||
let turntable = create_signal(false); /* BENCHMARKING */
|
||||
|
||||
// change listener
|
||||
let scene_changed = create_signal(true);
|
||||
create_effect(move || {
|
||||
state.assembly.elements.track();
|
||||
state.selection.track();
|
||||
scene_changed.set(true);
|
||||
});
|
||||
@ -121,7 +119,6 @@ pub fn Display() -> View {
|
||||
// viewpoint
|
||||
const ROT_SPEED: f64 = 0.4; // in radians per second
|
||||
const ZOOM_SPEED: f64 = 0.15; // multiplicative rate per second
|
||||
const TURNTABLE_SPEED: f64 = 0.1; /* BENCHMARKING */
|
||||
let mut orientation = DMatrix::<f64>::identity(5, 5);
|
||||
let mut rotation = DMatrix::<f64>::identity(5, 5);
|
||||
let mut location_z: f64 = 5.0;
|
||||
@ -244,7 +241,6 @@ pub fn Display() -> View {
|
||||
let roll_cw_val = roll_cw.get();
|
||||
let zoom_in_val = zoom_in.get();
|
||||
let zoom_out_val = zoom_out.get();
|
||||
let turntable_val = turntable.get(); /* BENCHMARKING */
|
||||
|
||||
// update the assembly's orientation
|
||||
let ang_vel = {
|
||||
@ -256,10 +252,6 @@ pub fn Display() -> View {
|
||||
} else {
|
||||
Vector3::zeros()
|
||||
}
|
||||
} /* BENCHMARKING */ + if turntable_val {
|
||||
Vector3::new(0.0, TURNTABLE_SPEED, 0.0)
|
||||
} else {
|
||||
Vector3::zeros()
|
||||
};
|
||||
let mut rotation_sp = rotation.fixed_view_mut::<3, 3>(0, 0);
|
||||
rotation_sp.copy_from(
|
||||
@ -359,7 +351,6 @@ pub fn Display() -> View {
|
||||
|| roll_ccw_val != 0.0
|
||||
|| zoom_in_val != 0.0
|
||||
|| zoom_out_val != 0.0
|
||||
|| turntable_val /* BENCHMARKING */
|
||||
);
|
||||
} else {
|
||||
frames_since_last_sample = 0;
|
||||
@ -409,10 +400,6 @@ pub fn Display() -> View {
|
||||
pitch_up.set(0.0);
|
||||
pitch_down.set(0.0);
|
||||
} else {
|
||||
if event.key() == "Enter" { /* BENCHMARKING */
|
||||
turntable.set_fn(|turn| !turn);
|
||||
scene_changed.set(true);
|
||||
}
|
||||
set_nav_signal(event, 1.0);
|
||||
}
|
||||
},
|
||||
|
@ -1,27 +0,0 @@
|
||||
use nalgebra::DVector;
|
||||
|
||||
// the sphere with the given center and radius, with inward-pointing normals
|
||||
pub fn sphere(center_x: f64, center_y: f64, center_z: f64, radius: f64) -> DVector<f64> {
|
||||
let center_norm_sq = center_x * center_x + center_y * center_y + center_z * center_z;
|
||||
DVector::from_column_slice(&[
|
||||
center_x / radius,
|
||||
center_y / radius,
|
||||
center_z / radius,
|
||||
0.5 / radius,
|
||||
0.5 * (center_norm_sq / radius - radius)
|
||||
])
|
||||
}
|
||||
|
||||
// the sphere of curvature `curv` whose closest point to the origin has position
|
||||
// `off * dir` and normal `dir`, where `dir` is a unit vector. setting the
|
||||
// curvature to zero gives a plane
|
||||
pub fn sphere_with_offset(dir_x: f64, dir_y: f64, dir_z: f64, off: f64, curv: f64) -> DVector<f64> {
|
||||
let norm_sp = 1.0 + off * curv;
|
||||
DVector::from_column_slice(&[
|
||||
norm_sp * dir_x,
|
||||
norm_sp * dir_y,
|
||||
norm_sp * dir_z,
|
||||
0.5 * curv,
|
||||
off * (1.0 + 0.5 * off * curv)
|
||||
])
|
||||
}
|
@ -1,14 +1,14 @@
|
||||
mod add_remove;
|
||||
mod assembly;
|
||||
mod display;
|
||||
mod engine;
|
||||
mod outline;
|
||||
|
||||
use nalgebra::DVector;
|
||||
use rustc_hash::FxHashSet;
|
||||
use sycamore::prelude::*;
|
||||
|
||||
use add_remove::AddRemove;
|
||||
use assembly::Assembly;
|
||||
use assembly::{Assembly, Constraint, Element};
|
||||
use display::Display;
|
||||
use outline::Outline;
|
||||
|
||||
@ -29,7 +29,45 @@ impl AppState {
|
||||
|
||||
fn main() {
|
||||
sycamore::render(|| {
|
||||
provide_context(AppState::new());
|
||||
let state = AppState::new();
|
||||
let key_a = state.assembly.elements.update(
|
||||
|elts| elts.insert(
|
||||
Element {
|
||||
id: String::from("wing_a"),
|
||||
label: String::from("Wing A"),
|
||||
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]),
|
||||
constraints: FxHashSet::default()
|
||||
}
|
||||
)
|
||||
);
|
||||
let key_b = state.assembly.elements.update(
|
||||
|elts| elts.insert(
|
||||
Element {
|
||||
id: String::from("wing_b"),
|
||||
label: String::from("Wing B"),
|
||||
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]),
|
||||
constraints: FxHashSet::default()
|
||||
},
|
||||
)
|
||||
);
|
||||
state.assembly.elements.update(
|
||||
|elts| elts.insert(
|
||||
Element {
|
||||
id: String::from("central"),
|
||||
label: String::from("Central"),
|
||||
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]),
|
||||
constraints: FxHashSet::default()
|
||||
}
|
||||
)
|
||||
);
|
||||
state.assembly.insert_constraint(Constraint {
|
||||
args: (key_a, key_b),
|
||||
rep: 0.5
|
||||
});
|
||||
provide_context(state);
|
||||
|
||||
view! {
|
||||
div(id="sidebar") {
|
||||
|
@ -148,12 +148,7 @@ pub fn Outline() -> View {
|
||||
}
|
||||
}
|
||||
},
|
||||
key=|(key, elt)| (
|
||||
key.clone(),
|
||||
elt.id.clone(),
|
||||
elt.label.clone(),
|
||||
elt.constraints.clone()
|
||||
)
|
||||
key=|(key, _)| key.clone()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user