1mod scalar_quantity;
4
5pub use scalar_quantity::*;
6
7use glam::{Mat4, UVec3, Vec3, Vec4};
8use polyscope_core::pick::PickResult;
9use polyscope_core::quantity::Quantity;
10use polyscope_core::structure::{HasQuantities, RenderContext, Structure};
11use polyscope_render::CurveNetworkRenderData;
12
13pub struct VolumeGrid {
21 name: String,
22
23 node_dim: UVec3, bound_min: Vec3, bound_max: Vec3, enabled: bool,
30 transform: Mat4,
31 quantities: Vec<Box<dyn Quantity>>,
32
33 color: Vec4,
35 edge_color: Vec4,
36 edge_width: f32,
37 cube_size_factor: f32,
38
39 render_data: Option<CurveNetworkRenderData>,
41}
42
43impl VolumeGrid {
44 pub fn new(name: impl Into<String>, node_dim: UVec3, bound_min: Vec3, bound_max: Vec3) -> Self {
52 Self {
53 name: name.into(),
54 node_dim,
55 bound_min,
56 bound_max,
57 enabled: true,
58 transform: Mat4::IDENTITY,
59 quantities: Vec::new(),
60 color: Vec4::new(0.5, 0.5, 0.5, 1.0),
61 edge_color: Vec4::new(0.0, 0.0, 0.0, 1.0),
62 edge_width: 1.0,
63 cube_size_factor: 0.0,
64 render_data: None,
65 }
66 }
67
68 pub fn new_uniform(
70 name: impl Into<String>,
71 dim: u32,
72 bound_min: Vec3,
73 bound_max: Vec3,
74 ) -> Self {
75 Self::new(name, UVec3::splat(dim), bound_min, bound_max)
76 }
77
78 #[must_use]
80 pub fn node_dim(&self) -> UVec3 {
81 self.node_dim
82 }
83
84 #[must_use]
86 pub fn cell_dim(&self) -> UVec3 {
87 self.node_dim.saturating_sub(UVec3::ONE)
88 }
89
90 #[must_use]
92 pub fn num_nodes(&self) -> u64 {
93 u64::from(self.node_dim.x) * u64::from(self.node_dim.y) * u64::from(self.node_dim.z)
94 }
95
96 #[must_use]
98 pub fn num_cells(&self) -> u64 {
99 let cell_dim = self.cell_dim();
100 u64::from(cell_dim.x) * u64::from(cell_dim.y) * u64::from(cell_dim.z)
101 }
102
103 #[must_use]
105 pub fn bound_min(&self) -> Vec3 {
106 self.bound_min
107 }
108
109 #[must_use]
111 pub fn bound_max(&self) -> Vec3 {
112 self.bound_max
113 }
114
115 #[must_use]
117 pub fn grid_spacing(&self) -> Vec3 {
118 let cell_dim = self.cell_dim().as_vec3();
119 (self.bound_max - self.bound_min) / cell_dim.max(Vec3::ONE)
120 }
121
122 #[must_use]
124 pub fn flatten_node_index(&self, i: u32, j: u32, k: u32) -> u64 {
125 u64::from(i)
126 + (u64::from(j) * u64::from(self.node_dim.x))
127 + (u64::from(k) * u64::from(self.node_dim.x) * u64::from(self.node_dim.y))
128 }
129
130 #[must_use]
132 pub fn unflatten_node_index(&self, idx: u64) -> UVec3 {
133 let x = idx % u64::from(self.node_dim.x);
134 let y = (idx / u64::from(self.node_dim.x)) % u64::from(self.node_dim.y);
135 let z = idx / (u64::from(self.node_dim.x) * u64::from(self.node_dim.y));
136 UVec3::new(x as u32, y as u32, z as u32)
137 }
138
139 #[must_use]
141 pub fn position_of_node(&self, i: u32, j: u32, k: u32) -> Vec3 {
142 let cell_dim = self.cell_dim().as_vec3().max(Vec3::ONE);
143 let t = Vec3::new(i as f32, j as f32, k as f32) / cell_dim;
144 self.bound_min + t * (self.bound_max - self.bound_min)
145 }
146
147 #[must_use]
149 pub fn color(&self) -> Vec4 {
150 self.color
151 }
152
153 pub fn set_color(&mut self, color: Vec3) -> &mut Self {
155 self.color = color.extend(1.0);
156 self
157 }
158
159 #[must_use]
161 pub fn edge_color(&self) -> Vec4 {
162 self.edge_color
163 }
164
165 pub fn set_edge_color(&mut self, color: Vec3) -> &mut Self {
167 self.edge_color = color.extend(1.0);
168 self
169 }
170
171 #[must_use]
173 pub fn edge_width(&self) -> f32 {
174 self.edge_width
175 }
176
177 pub fn set_edge_width(&mut self, width: f32) -> &mut Self {
179 self.edge_width = width;
180 self
181 }
182
183 #[must_use]
185 pub fn cube_size_factor(&self) -> f32 {
186 self.cube_size_factor
187 }
188
189 pub fn set_cube_size_factor(&mut self, factor: f32) -> &mut Self {
191 self.cube_size_factor = factor.clamp(0.0, 1.0);
192 self
193 }
194
195 pub fn quantities_mut(&mut self) -> &mut [Box<dyn Quantity>] {
197 &mut self.quantities
198 }
199
200 fn generate_bbox_wireframe(&self) -> (Vec<Vec3>, Vec<[u32; 2]>) {
202 let min = self.bound_min;
203 let max = self.bound_max;
204
205 let nodes = vec![
207 Vec3::new(min.x, min.y, min.z), Vec3::new(max.x, min.y, min.z), Vec3::new(max.x, max.y, min.z), Vec3::new(min.x, max.y, min.z), Vec3::new(min.x, min.y, max.z), Vec3::new(max.x, min.y, max.z), Vec3::new(max.x, max.y, max.z), Vec3::new(min.x, max.y, max.z), ];
216
217 let edges = vec![
219 [0, 1],
221 [1, 2],
222 [2, 3],
223 [3, 0],
224 [4, 5],
226 [5, 6],
227 [6, 7],
228 [7, 4],
229 [0, 4],
231 [1, 5],
232 [2, 6],
233 [3, 7],
234 ];
235
236 (nodes, edges)
237 }
238
239 pub fn init_render_data(
241 &mut self,
242 device: &wgpu::Device,
243 bind_group_layout: &wgpu::BindGroupLayout,
244 camera_buffer: &wgpu::Buffer,
245 queue: &wgpu::Queue,
246 ) {
247 let (nodes, edges) = self.generate_bbox_wireframe();
248
249 let edge_tail_inds: Vec<u32> = edges.iter().map(|e| e[0]).collect();
250 let edge_tip_inds: Vec<u32> = edges.iter().map(|e| e[1]).collect();
251
252 let render_data = CurveNetworkRenderData::new(
253 device,
254 bind_group_layout,
255 camera_buffer,
256 &nodes,
257 &edge_tail_inds,
258 &edge_tip_inds,
259 );
260
261 let uniforms = polyscope_render::CurveNetworkUniforms {
263 color: self.edge_color.to_array(),
264 radius: self.edge_width * 0.002,
265 radius_is_relative: 1,
266 render_mode: 0,
267 _padding: 0.0,
268 };
269 render_data.update_uniforms(queue, &uniforms);
270
271 self.render_data = Some(render_data);
272 }
273
274 #[must_use]
276 pub fn render_data(&self) -> Option<&CurveNetworkRenderData> {
277 self.render_data.as_ref()
278 }
279
280 pub fn update_gpu_buffers(&self, queue: &wgpu::Queue) {
282 if let Some(render_data) = &self.render_data {
283 let uniforms = polyscope_render::CurveNetworkUniforms {
284 color: self.edge_color.to_array(),
285 radius: self.edge_width * 0.002,
286 radius_is_relative: 1,
287 render_mode: 0,
288 _padding: 0.0,
289 };
290 render_data.update_uniforms(queue, &uniforms);
291 }
292 }
293
294 pub fn add_node_scalar_quantity(
296 &mut self,
297 name: impl Into<String>,
298 values: Vec<f32>,
299 ) -> &mut Self {
300 let quantity = VolumeGridNodeScalarQuantity::new(
301 name,
302 self.name.clone(),
303 values,
304 self.node_dim,
305 self.bound_min,
306 self.bound_max,
307 );
308 self.add_quantity(Box::new(quantity));
309 self
310 }
311
312 pub fn add_cell_scalar_quantity(
314 &mut self,
315 name: impl Into<String>,
316 values: Vec<f32>,
317 ) -> &mut Self {
318 let quantity = VolumeGridCellScalarQuantity::new(
319 name,
320 self.name.clone(),
321 values,
322 self.cell_dim(),
323 self.bound_min,
324 self.bound_max,
325 );
326 self.add_quantity(Box::new(quantity));
327 self
328 }
329
330 pub fn build_egui_ui(&mut self, ui: &mut egui::Ui, colormap_names: &[&str]) {
336 let default_names = ["viridis", "blues", "reds", "coolwarm", "rainbow"];
337 let names = if colormap_names.is_empty() {
338 &default_names[..]
339 } else {
340 colormap_names
341 };
342 ui.label(format!(
344 "{}x{}x{} grid ({} nodes)",
345 self.node_dim.x,
346 self.node_dim.y,
347 self.node_dim.z,
348 self.num_nodes()
349 ));
350
351 ui.horizontal(|ui| {
353 ui.label("Edge Color:");
354 let mut color = [self.edge_color.x, self.edge_color.y, self.edge_color.z];
355 if ui.color_edit_button_rgb(&mut color).changed() {
356 self.edge_color = Vec4::new(color[0], color[1], color[2], self.edge_color.w);
357 }
358 });
359
360 if !self.quantities.is_empty() {
362 ui.separator();
363 ui.label("Quantities:");
364 for quantity in &mut self.quantities {
365 if let Some(sq) = quantity
366 .as_any_mut()
367 .downcast_mut::<VolumeGridNodeScalarQuantity>()
368 {
369 sq.build_egui_ui(ui, names);
370 } else if let Some(sq) = quantity
371 .as_any_mut()
372 .downcast_mut::<VolumeGridCellScalarQuantity>()
373 {
374 sq.build_egui_ui(ui, names);
375 }
376 }
377 }
378 }
379}
380
381impl Structure for VolumeGrid {
382 fn as_any(&self) -> &dyn std::any::Any {
383 self
384 }
385
386 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
387 self
388 }
389
390 fn name(&self) -> &str {
391 &self.name
392 }
393
394 fn type_name(&self) -> &'static str {
395 "VolumeGrid"
396 }
397
398 fn bounding_box(&self) -> Option<(Vec3, Vec3)> {
399 Some((self.bound_min, self.bound_max))
400 }
401
402 fn length_scale(&self) -> f32 {
403 (self.bound_max - self.bound_min).length()
404 }
405
406 fn transform(&self) -> Mat4 {
407 self.transform
408 }
409
410 fn set_transform(&mut self, transform: Mat4) {
411 self.transform = transform;
412 }
413
414 fn is_enabled(&self) -> bool {
415 self.enabled
416 }
417
418 fn set_enabled(&mut self, enabled: bool) {
419 self.enabled = enabled;
420 }
421
422 fn draw(&self, _ctx: &mut dyn RenderContext) {
423 }
425
426 fn draw_pick(&self, _ctx: &mut dyn RenderContext) {
427 }
429
430 fn build_ui(&mut self, _ui: &dyn std::any::Any) {
431 }
433
434 fn build_pick_ui(&self, _ui: &dyn std::any::Any, _pick: &PickResult) {
435 }
437
438 fn clear_gpu_resources(&mut self) {
439 self.render_data = None;
440 for quantity in &mut self.quantities {
441 quantity.clear_gpu_resources();
442 }
443 }
444
445 fn refresh(&mut self) {
446 self.render_data = None;
447 for quantity in &mut self.quantities {
448 quantity.refresh();
449 }
450 }
451}
452
453impl HasQuantities for VolumeGrid {
454 fn add_quantity(&mut self, quantity: Box<dyn Quantity>) {
455 self.quantities.push(quantity);
456 }
457
458 fn get_quantity(&self, name: &str) -> Option<&dyn Quantity> {
459 self.quantities
460 .iter()
461 .find(|q| q.name() == name)
462 .map(std::convert::AsRef::as_ref)
463 }
464
465 fn get_quantity_mut(&mut self, name: &str) -> Option<&mut Box<dyn Quantity>> {
466 self.quantities.iter_mut().find(|q| q.name() == name)
467 }
468
469 fn remove_quantity(&mut self, name: &str) -> Option<Box<dyn Quantity>> {
470 let idx = self.quantities.iter().position(|q| q.name() == name)?;
471 Some(self.quantities.remove(idx))
472 }
473
474 fn quantities(&self) -> &[Box<dyn Quantity>] {
475 &self.quantities
476 }
477}
478
479#[cfg(test)]
480mod tests {
481 use super::*;
482
483 #[test]
484 fn test_volume_grid_creation() {
485 let grid = VolumeGrid::new("test", UVec3::new(10, 20, 30), Vec3::ZERO, Vec3::ONE);
486 assert_eq!(grid.node_dim(), UVec3::new(10, 20, 30));
487 assert_eq!(grid.cell_dim(), UVec3::new(9, 19, 29));
488 assert_eq!(grid.num_nodes(), 10 * 20 * 30);
489 assert_eq!(grid.num_cells(), 9 * 19 * 29);
490 }
491
492 #[test]
493 fn test_index_conversion() {
494 let grid = VolumeGrid::new("test", UVec3::new(5, 6, 7), Vec3::ZERO, Vec3::ONE);
495
496 let idx = grid.flatten_node_index(2, 3, 4);
497 let uvec = grid.unflatten_node_index(idx);
498 assert_eq!(uvec, UVec3::new(2, 3, 4));
499 }
500
501 #[test]
502 fn test_node_position() {
503 let grid = VolumeGrid::new(
504 "test",
505 UVec3::new(3, 3, 3),
506 Vec3::ZERO,
507 Vec3::new(2.0, 2.0, 2.0),
508 );
509
510 let p = grid.position_of_node(0, 0, 0);
511 assert!((p - Vec3::ZERO).length() < 1e-6);
512
513 let p = grid.position_of_node(2, 2, 2);
514 assert!((p - Vec3::new(2.0, 2.0, 2.0)).length() < 1e-6);
515
516 let p = grid.position_of_node(1, 1, 1);
517 assert!((p - Vec3::ONE).length() < 1e-6);
518 }
519}