diff --git a/app-proto/src/display.rs b/app-proto/src/display.rs index 59d0dca..14b132a 100644 --- a/app-proto/src/display.rs +++ b/app-proto/src/display.rs @@ -4,7 +4,6 @@ use sycamore::{prelude::*, motion::create_raf}; use web_sys::{ console, window, - Element, KeyboardEvent, MouseEvent, WebGl2RenderingContext, @@ -15,7 +14,77 @@ use web_sys::{ wasm_bindgen::{JsCast, JsValue} }; -use crate::{AppState, assembly::{ElementKey, ElementMotion}}; +use crate::{AppState, assembly::{Element, ElementKey, ElementColor, ElementMotion}}; + +// --- scene data --- + +struct SceneSpheres { + representations: Vec>, + colors: Vec, + highlights: Vec +} + +impl SceneSpheres { + fn new() -> SceneSpheres{ + SceneSpheres { + representations: Vec::new(), + colors: Vec::new(), + highlights: Vec::new() + } + } + + fn len_i32(&self) -> i32 { + self.representations.len().try_into().expect("Number of spheres must fit in a 32-bit integer") + } + + fn push(&mut self, representation: DVector, color: ElementColor, highlight: f32) { + self.representations.push(representation); + self.colors.push(color); + self.highlights.push(highlight); + } +} + +struct ScenePoints { + representations: Vec> +} + +impl ScenePoints { + fn new() -> ScenePoints { + ScenePoints { + representations: Vec::new() + } + } +} + +struct Scene { + spheres: SceneSpheres, + points: ScenePoints +} + +impl Scene { + fn new() -> Scene { + Scene { + spheres: SceneSpheres::new(), + points: ScenePoints::new() + } + } +} + +trait DisplayItem { + fn show(&self, scene: &mut Scene, selected: bool); +} + +impl DisplayItem for Element { + fn show(&self, scene: &mut Scene, selected: bool) { + const HIGHLIGHT: f32 = 0.2; /* SCAFFOLDING */ + let representation = self.representation.get_clone_untracked(); + let color = if selected { self.color.map(|channel| 0.2 + 0.8*channel) } else { self.color }; + let highlight = if selected { 1.0 } else { HIGHLIGHT }; + scene.spheres.push(representation, color, highlight); + } +} + +// --- WebGL utilities --- fn compile_shader( context: &WebGl2RenderingContext, @@ -140,7 +209,7 @@ fn load_new_buffer( // the direction in camera space that a mouse event is pointing along fn event_dir(event: &MouseEvent) -> Vector3 { - let target: Element = event.target().unwrap().unchecked_into(); + let target: web_sys::Element = event.target().unwrap().unchecked_into(); let rect = target.get_bounding_client_rect(); let width = rect.width(); let height = rect.height(); @@ -157,6 +226,8 @@ fn event_dir(event: &MouseEvent) -> Vector3 { ) } +// --- display component --- + #[component] pub fn Display() -> View { let state = use_context::(); @@ -225,7 +296,6 @@ pub fn Display() -> View { // display parameters const OPACITY: f32 = 0.5; /* SCAFFOLDING */ - const HIGHLIGHT: f32 = 0.2; /* SCAFFOLDING */ const LAYER_THRESHOLD: i32 = 0; /* DEBUG */ const DEBUG_MODE: i32 = 0; /* DEBUG */ @@ -421,6 +491,8 @@ pub fn Display() -> View { // --- get the assembly --- + let mut scene = Scene::new(); + // find the map from assembly space to world space let location = { let u = -location_z; @@ -435,50 +507,24 @@ pub fn Display() -> View { let asm_to_world = &location * &orientation; // get the spheres - let ( - sphere_cnt, - sphere_reps_world, - sphere_colors, - sphere_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| &asm_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::>() - ) - }); + state.assembly.elements.with_untracked( + |elts| for (key, elt) in elts { + let selected = state.selection.with(|sel| sel.contains(&key)); + elt.show(&mut scene, selected); + } + ); + let sphere_cnt = scene.spheres.len_i32(); + + // write the spheres in world coordinates + let sphere_reps_world: Vec<_> = scene.spheres.representations.into_iter().map( + |rep| &asm_to_world * rep + ).collect(); /* SCAFFOLDING */ // get the points - let point_positions = { + scene.points.representations.append({ use crate::engine::point; - - /* DEBUG */ - // hard-code the origin and the centers of the spheres in - // the general test assembly - let point_reps = [ + &mut vec![ point(0.0, 0.0, 0.0), point(0.5, 0.5, 0.0), point(-0.5, -0.5, 0.0), @@ -486,12 +532,16 @@ pub fn Display() -> View { point(0.5, -0.5, 0.0), point(0.0, 0.15, 1.0), point(0.0, -0.15, -1.0) - ]; - - let asm_to_world_sp = asm_to_world.rows(0, SPACE_DIM); - let point_reps_world_sp = point_reps.map(|rep| &asm_to_world_sp * rep); - DMatrix::from_columns(&point_reps_world_sp).cast::() - }; + ] + }); + + // write the points in world coordinates + let asm_to_world_sp = asm_to_world.rows(0, SPACE_DIM); + let point_positions = DMatrix::from_columns( + &scene.points.representations.into_iter().map( + |rep| &asm_to_world_sp * rep + ).collect::>().as_slice() + ).cast::(); // --- draw the spheres --- @@ -518,11 +568,11 @@ pub fn Display() -> View { ); ctx.uniform3fv_with_f32_array( sphere_color_locs[n].as_ref(), - &sphere_colors[n] + &scene.spheres.colors[n] ); ctx.uniform1f( sphere_highlight_locs[n].as_ref(), - sphere_highlights[n] + scene.spheres.highlights[n] ); }