Encapsulate scene data

This commit is contained in:
Aaron Fenyes 2025-04-22 15:55:08 -07:00
parent d5eaf11a17
commit cedb1d5b83

View file

@ -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<DVector<f64>>,
colors: Vec<ElementColor>,
highlights: Vec<f32>
}
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<f64>, color: ElementColor, highlight: f32) {
self.representations.push(representation);
self.colors.push(color);
self.highlights.push(highlight);
}
}
struct ScenePoints {
representations: Vec<DVector<f64>>
}
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<f64> {
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<f64> {
)
}
// --- display component ---
#[component]
pub fn Display() -> View {
let state = use_context::<AppState>();
@ -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,
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();
// representation vectors in world coordinates
elts.iter().map(
|(_, elt)| elt.representation.with(|rep| &asm_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<_>>()
)
});
// 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::<f32>()
};
// 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::<Vec<_>>().as_slice()
).cast::<f32>();
// --- 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]
);
}