1use glam::{UVec3, Vec3};
4use polyscope_core::quantity::{Quantity, QuantityKind};
5use polyscope_core::{McmMesh, marching_cubes};
6use polyscope_render::{GridcubePickUniforms, GridcubeRenderData, IsosurfaceRenderData};
7use wgpu::util::DeviceExt;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum VolumeGridVizMode {
12 Gridcube,
14 Isosurface,
16}
17
18pub struct VolumeGridNodeScalarQuantity {
20 name: String,
21 structure_name: String,
22 values: Vec<f32>,
23 node_dim: UVec3,
24 enabled: bool,
25
26 color_map: String,
28 data_min: f32,
29 data_max: f32,
30
31 viz_mode: VolumeGridVizMode,
33
34 gridcube_render_data: Option<GridcubeRenderData>,
36 gridcube_dirty: bool,
37
38 isosurface_level: f32,
40 isosurface_color: Vec3,
41 isosurface_render_data: Option<IsosurfaceRenderData>,
42 isosurface_mesh_cache: Option<McmMesh>,
43 isosurface_dirty: bool,
44
45 bound_min: Vec3,
47 bound_max: Vec3,
48
49 register_as_mesh_requested: bool,
51
52 pick_uniform_buffer: Option<wgpu::Buffer>,
54 pick_bind_group: Option<wgpu::BindGroup>,
55 global_start: u32,
56}
57
58impl VolumeGridNodeScalarQuantity {
59 pub fn new(
61 name: impl Into<String>,
62 structure_name: impl Into<String>,
63 values: Vec<f32>,
64 node_dim: UVec3,
65 bound_min: Vec3,
66 bound_max: Vec3,
67 ) -> Self {
68 let (data_min, data_max) = Self::compute_range(&values);
69 let isosurface_level = (data_min + data_max) * 0.5;
70 Self {
71 name: name.into(),
72 structure_name: structure_name.into(),
73 values,
74 node_dim,
75 enabled: false,
76 color_map: "viridis".to_string(),
77 data_min,
78 data_max,
79 viz_mode: VolumeGridVizMode::Gridcube,
80 gridcube_render_data: None,
81 gridcube_dirty: true,
82 isosurface_level,
83 isosurface_color: Vec3::new(0.047, 0.451, 0.690), isosurface_render_data: None,
85 isosurface_mesh_cache: None,
86 isosurface_dirty: true,
87 bound_min,
88 bound_max,
89 register_as_mesh_requested: false,
90 pick_uniform_buffer: None,
91 pick_bind_group: None,
92 global_start: 0,
93 }
94 }
95
96 fn compute_range(values: &[f32]) -> (f32, f32) {
97 let mut min = f32::MAX;
98 let mut max = f32::MIN;
99 for &v in values {
100 if v.is_finite() {
101 min = min.min(v);
102 max = max.max(v);
103 }
104 }
105 if min > max { (0.0, 1.0) } else { (min, max) }
106 }
107
108 #[must_use]
110 pub fn values(&self) -> &[f32] {
111 &self.values
112 }
113
114 #[must_use]
116 pub fn node_dim(&self) -> UVec3 {
117 self.node_dim
118 }
119
120 #[must_use]
122 pub fn get(&self, i: u32, j: u32, k: u32) -> f32 {
123 let idx = i as usize
124 + j as usize * self.node_dim.x as usize
125 + k as usize * self.node_dim.x as usize * self.node_dim.y as usize;
126 self.values.get(idx).copied().unwrap_or(0.0)
127 }
128
129 #[must_use]
131 pub fn color_map(&self) -> &str {
132 &self.color_map
133 }
134
135 pub fn set_color_map(&mut self, name: impl Into<String>) -> &mut Self {
137 self.color_map = name.into();
138 self.gridcube_dirty = true;
139 self
140 }
141
142 #[must_use]
144 pub fn data_range(&self) -> (f32, f32) {
145 (self.data_min, self.data_max)
146 }
147
148 pub fn set_data_range(&mut self, min: f32, max: f32) -> &mut Self {
150 self.data_min = min;
151 self.data_max = max;
152 self
153 }
154
155 #[must_use]
159 pub fn viz_mode(&self) -> VolumeGridVizMode {
160 self.viz_mode
161 }
162
163 pub fn set_viz_mode(&mut self, mode: VolumeGridVizMode) -> &mut Self {
165 self.viz_mode = mode;
166 self
167 }
168
169 #[must_use]
173 pub fn isosurface_level(&self) -> f32 {
174 self.isosurface_level
175 }
176
177 pub fn set_isosurface_level(&mut self, level: f32) -> &mut Self {
179 self.isosurface_level = level;
180 self.isosurface_dirty = true;
181 self
183 }
184
185 #[must_use]
187 pub fn isosurface_color(&self) -> Vec3 {
188 self.isosurface_color
189 }
190
191 pub fn set_isosurface_color(&mut self, color: Vec3) -> &mut Self {
193 self.isosurface_color = color;
194 self
195 }
196
197 #[must_use]
199 pub fn isosurface_dirty(&self) -> bool {
200 self.isosurface_dirty
201 }
202
203 #[must_use]
205 pub fn gridcube_dirty(&self) -> bool {
206 self.gridcube_dirty
207 }
208
209 pub fn extract_isosurface(&mut self) -> &McmMesh {
214 if self.isosurface_mesh_cache.is_none() || self.isosurface_dirty {
215 let nx = self.node_dim.x;
216 let ny = self.node_dim.y;
217 let nz = self.node_dim.z;
218
219 let mut mesh = marching_cubes(&self.values, self.isosurface_level, nx, ny, nz);
220
221 let cell_dim = Vec3::new(
225 (nx - 1).max(1) as f32,
226 (ny - 1).max(1) as f32,
227 (nz - 1).max(1) as f32,
228 );
229 let spacing = (self.bound_max - self.bound_min) / cell_dim;
230
231 for v in &mut mesh.vertices {
232 *v = Vec3::new(
236 v.x * spacing.x + self.bound_min.x,
237 v.y * spacing.y + self.bound_min.y,
238 v.z * spacing.z + self.bound_min.z,
239 );
240 }
241
242 for n in &mut mesh.normals {
244 *n = Vec3::new(n.x / spacing.x, n.y / spacing.y, n.z / spacing.z);
246 let len = n.length();
247 if len > 0.0 {
248 *n /= len;
249 }
250 }
251
252 self.isosurface_mesh_cache = Some(mesh);
253 self.isosurface_dirty = false;
254 }
255 self.isosurface_mesh_cache.as_ref().unwrap()
256 }
257
258 #[must_use]
260 pub fn isosurface_mesh(&self) -> Option<&McmMesh> {
261 self.isosurface_mesh_cache.as_ref()
262 }
263
264 #[must_use]
268 pub fn gridcube_render_data(&self) -> Option<&GridcubeRenderData> {
269 self.gridcube_render_data.as_ref()
270 }
271
272 pub fn gridcube_render_data_mut(&mut self) -> Option<&mut GridcubeRenderData> {
274 self.gridcube_render_data.as_mut()
275 }
276
277 pub fn set_gridcube_render_data(&mut self, data: GridcubeRenderData) {
279 self.gridcube_render_data = Some(data);
280 self.gridcube_dirty = false;
281 }
282
283 #[must_use]
285 pub fn isosurface_render_data(&self) -> Option<&IsosurfaceRenderData> {
286 self.isosurface_render_data.as_ref()
287 }
288
289 pub fn isosurface_render_data_mut(&mut self) -> Option<&mut IsosurfaceRenderData> {
291 self.isosurface_render_data.as_mut()
292 }
293
294 pub fn set_isosurface_render_data(&mut self, data: IsosurfaceRenderData) {
296 self.isosurface_render_data = Some(data);
297 self.isosurface_dirty = false;
298 }
299
300 pub fn clear_isosurface_render_data(&mut self) {
302 self.isosurface_render_data = None;
303 self.isosurface_dirty = false;
304 }
305
306 pub fn init_pick_resources(
312 &mut self,
313 device: &wgpu::Device,
314 pick_bind_group_layout: &wgpu::BindGroupLayout,
315 camera_buffer: &wgpu::Buffer,
316 global_start: u32,
317 ) {
318 self.global_start = global_start;
319
320 let Some(gridcube_rd) = &self.gridcube_render_data else {
321 return;
322 };
323
324 let uniforms = GridcubePickUniforms {
325 global_start,
326 cube_size_factor: 1.0,
327 ..Default::default()
328 };
329
330 let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
331 label: Some("gridcube node pick uniforms"),
332 contents: bytemuck::cast_slice(&[uniforms]),
333 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
334 });
335
336 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
337 label: Some("gridcube node pick bind group"),
338 layout: pick_bind_group_layout,
339 entries: &[
340 wgpu::BindGroupEntry {
341 binding: 0,
342 resource: camera_buffer.as_entire_binding(),
343 },
344 wgpu::BindGroupEntry {
345 binding: 1,
346 resource: uniform_buffer.as_entire_binding(),
347 },
348 wgpu::BindGroupEntry {
349 binding: 2,
350 resource: gridcube_rd.position_buffer.as_entire_binding(),
351 },
352 ],
353 });
354
355 self.pick_uniform_buffer = Some(uniform_buffer);
356 self.pick_bind_group = Some(bind_group);
357 }
358
359 #[must_use]
361 pub fn pick_bind_group(&self) -> Option<&wgpu::BindGroup> {
362 self.pick_bind_group.as_ref()
363 }
364
365 pub fn update_pick_uniforms(
367 &self,
368 queue: &wgpu::Queue,
369 model: [[f32; 4]; 4],
370 cube_size_factor: f32,
371 ) {
372 if let Some(buffer) = &self.pick_uniform_buffer {
373 let uniforms = GridcubePickUniforms {
374 model,
375 global_start: self.global_start,
376 cube_size_factor,
377 ..Default::default()
378 };
379 queue.write_buffer(buffer, 0, bytemuck::cast_slice(&[uniforms]));
380 }
381 }
382
383 #[must_use]
385 pub fn num_pick_elements(&self) -> u32 {
386 self.gridcube_render_data
387 .as_ref()
388 .map_or(0, |rd| rd.num_instances)
389 }
390
391 #[must_use]
393 pub fn pick_total_vertices(&self) -> u32 {
394 self.gridcube_render_data
395 .as_ref()
396 .map_or(0, GridcubeRenderData::total_vertices)
397 }
398
399 #[must_use]
401 pub fn register_as_mesh_requested(&self) -> bool {
402 self.register_as_mesh_requested
403 }
404
405 pub fn clear_register_as_mesh_request(&mut self) {
407 self.register_as_mesh_requested = false;
408 }
409
410 pub fn build_egui_ui(&mut self, ui: &mut egui::Ui, colormap_names: &[&str]) {
412 ui.horizontal(|ui| {
413 let mut enabled = self.enabled;
414 if ui.checkbox(&mut enabled, "").changed() {
415 self.enabled = enabled;
416 }
417 ui.label(&self.name);
418 ui.label(format!("[{:.3}, {:.3}]", self.data_min, self.data_max));
419 });
420
421 if self.enabled {
422 let indent_id = egui::Id::new(&self.name).with("node_scalar_indent");
423 ui.indent(indent_id, |ui| {
424 ui.horizontal(|ui| {
426 ui.label("Mode:");
427 if ui
428 .selectable_label(self.viz_mode == VolumeGridVizMode::Gridcube, "Gridcube")
429 .clicked()
430 {
431 self.viz_mode = VolumeGridVizMode::Gridcube;
432 }
433 if ui
434 .selectable_label(
435 self.viz_mode == VolumeGridVizMode::Isosurface,
436 "Isosurface",
437 )
438 .clicked()
439 {
440 self.viz_mode = VolumeGridVizMode::Isosurface;
441 }
442 });
443
444 match self.viz_mode {
445 VolumeGridVizMode::Gridcube => {
446 self.build_gridcube_ui(ui, colormap_names);
447 }
448 VolumeGridVizMode::Isosurface => {
449 self.build_isosurface_ui(ui);
450 }
451 }
452 });
453 }
454 }
455
456 fn build_gridcube_ui(&mut self, ui: &mut egui::Ui, colormap_names: &[&str]) {
457 if !colormap_names.is_empty() {
459 ui.horizontal(|ui| {
460 ui.label("Colormap:");
461 egui::ComboBox::from_id_salt(format!("{}_colormap", self.name))
462 .selected_text(&self.color_map)
463 .show_ui(ui, |ui| {
464 for &name in colormap_names {
465 if ui.selectable_label(self.color_map == name, name).clicked() {
466 self.color_map = name.to_string();
467 self.gridcube_dirty = true;
468 }
469 }
470 });
471 });
472 }
473
474 ui.horizontal(|ui| {
476 ui.label("Range:");
477 let mut min = self.data_min;
478 let mut max = self.data_max;
479 let speed = (max - min).abs() * 0.01;
480 let speed = if speed > 0.0 { speed } else { 0.01 };
481 ui.add(egui::DragValue::new(&mut min).speed(speed));
482 ui.label("–");
483 ui.add(egui::DragValue::new(&mut max).speed(speed));
484 if (min - self.data_min).abs() > f32::EPSILON
485 || (max - self.data_max).abs() > f32::EPSILON
486 {
487 self.data_min = min;
488 self.data_max = max;
489 }
490 });
491 }
492
493 fn build_isosurface_ui(&mut self, ui: &mut egui::Ui) {
494 egui::Grid::new(format!("{}_iso_grid", self.name))
495 .num_columns(2)
496 .show(ui, |ui| {
497 ui.label("Color:");
498 let mut color = [
499 self.isosurface_color.x,
500 self.isosurface_color.y,
501 self.isosurface_color.z,
502 ];
503 if ui.color_edit_button_rgb(&mut color).changed() {
504 self.isosurface_color = Vec3::new(color[0], color[1], color[2]);
505 }
506 ui.end_row();
507
508 ui.label("Level:");
509 let mut level = self.isosurface_level;
510 let (range_min, range_max) = (self.data_min, self.data_max);
511 if ui
512 .add(egui::Slider::new(&mut level, range_min..=range_max))
513 .changed()
514 {
515 self.isosurface_level = level;
516 self.isosurface_dirty = true;
517 }
518 ui.end_row();
519 });
520
521 if let Some(mesh) = &self.isosurface_mesh_cache {
523 ui.label(format!("{} tris", mesh.indices.len() / 3));
524 }
525
526 let has_cache = self.isosurface_mesh_cache.is_some();
528 if has_cache {
529 ui.columns(2, |cols| {
530 let w = cols[0].available_width();
531 let h = cols[0].spacing().interact_size.y;
532 if cols[0]
533 .add_sized([w, h], egui::Button::new("Refresh"))
534 .clicked()
535 {
536 self.isosurface_dirty = true;
537 self.isosurface_mesh_cache = None;
538 self.isosurface_render_data = None;
539 }
540 if cols[1]
541 .add_sized([w, h], egui::Button::new("Register Mesh"))
542 .clicked()
543 {
544 self.register_as_mesh_requested = true;
545 }
546 });
547 } else if ui.button("Refresh").clicked() {
548 self.isosurface_dirty = true;
549 self.isosurface_mesh_cache = None;
550 self.isosurface_render_data = None;
551 }
552 }
553}
554
555impl Quantity for VolumeGridNodeScalarQuantity {
556 fn name(&self) -> &str {
557 &self.name
558 }
559
560 fn structure_name(&self) -> &str {
561 &self.structure_name
562 }
563
564 fn kind(&self) -> QuantityKind {
565 QuantityKind::Scalar
566 }
567
568 fn is_enabled(&self) -> bool {
569 self.enabled
570 }
571
572 fn set_enabled(&mut self, enabled: bool) {
573 self.enabled = enabled;
574 }
575
576 fn data_size(&self) -> usize {
577 self.values.len()
578 }
579
580 fn build_ui(&mut self, _ui: &dyn std::any::Any) {
581 }
583
584 fn refresh(&mut self) {
585 self.gridcube_render_data = None;
586 self.gridcube_dirty = true;
587 self.isosurface_render_data = None;
588 self.isosurface_mesh_cache = None;
589 self.isosurface_dirty = true;
590 self.pick_uniform_buffer = None;
591 self.pick_bind_group = None;
592 }
593
594 fn clear_gpu_resources(&mut self) {
595 self.gridcube_render_data = None;
596 self.isosurface_render_data = None;
597 }
598
599 fn as_any(&self) -> &dyn std::any::Any {
600 self
601 }
602
603 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
604 self
605 }
606}
607
608pub struct VolumeGridCellScalarQuantity {
610 name: String,
611 structure_name: String,
612 values: Vec<f32>,
613 cell_dim: UVec3,
614 enabled: bool,
615
616 color_map: String,
618 data_min: f32,
619 data_max: f32,
620
621 gridcube_render_data: Option<GridcubeRenderData>,
623 gridcube_dirty: bool,
624
625 #[allow(dead_code)]
627 bound_min: Vec3,
628 #[allow(dead_code)]
629 bound_max: Vec3,
630
631 pick_uniform_buffer: Option<wgpu::Buffer>,
633 pick_bind_group: Option<wgpu::BindGroup>,
634 global_start: u32,
635}
636
637impl VolumeGridCellScalarQuantity {
638 pub fn new(
640 name: impl Into<String>,
641 structure_name: impl Into<String>,
642 values: Vec<f32>,
643 cell_dim: UVec3,
644 bound_min: Vec3,
645 bound_max: Vec3,
646 ) -> Self {
647 let (data_min, data_max) = Self::compute_range(&values);
648 Self {
649 name: name.into(),
650 structure_name: structure_name.into(),
651 values,
652 cell_dim,
653 enabled: false,
654 color_map: "viridis".to_string(),
655 data_min,
656 data_max,
657 gridcube_render_data: None,
658 gridcube_dirty: true,
659 bound_min,
660 bound_max,
661 pick_uniform_buffer: None,
662 pick_bind_group: None,
663 global_start: 0,
664 }
665 }
666
667 fn compute_range(values: &[f32]) -> (f32, f32) {
668 let mut min = f32::MAX;
669 let mut max = f32::MIN;
670 for &v in values {
671 if v.is_finite() {
672 min = min.min(v);
673 max = max.max(v);
674 }
675 }
676 if min > max { (0.0, 1.0) } else { (min, max) }
677 }
678
679 #[must_use]
681 pub fn values(&self) -> &[f32] {
682 &self.values
683 }
684
685 #[must_use]
687 pub fn cell_dim(&self) -> UVec3 {
688 self.cell_dim
689 }
690
691 #[must_use]
693 pub fn get(&self, i: u32, j: u32, k: u32) -> f32 {
694 let idx = i as usize
695 + j as usize * self.cell_dim.x as usize
696 + k as usize * self.cell_dim.x as usize * self.cell_dim.y as usize;
697 self.values.get(idx).copied().unwrap_or(0.0)
698 }
699
700 #[must_use]
702 pub fn color_map(&self) -> &str {
703 &self.color_map
704 }
705
706 pub fn set_color_map(&mut self, name: impl Into<String>) -> &mut Self {
708 self.color_map = name.into();
709 self.gridcube_dirty = true;
710 self
711 }
712
713 #[must_use]
715 pub fn data_range(&self) -> (f32, f32) {
716 (self.data_min, self.data_max)
717 }
718
719 pub fn set_data_range(&mut self, min: f32, max: f32) -> &mut Self {
721 self.data_min = min;
722 self.data_max = max;
723 self
724 }
725
726 #[must_use]
728 pub fn gridcube_dirty(&self) -> bool {
729 self.gridcube_dirty
730 }
731
732 #[must_use]
734 pub fn gridcube_render_data(&self) -> Option<&GridcubeRenderData> {
735 self.gridcube_render_data.as_ref()
736 }
737
738 pub fn gridcube_render_data_mut(&mut self) -> Option<&mut GridcubeRenderData> {
740 self.gridcube_render_data.as_mut()
741 }
742
743 pub fn set_gridcube_render_data(&mut self, data: GridcubeRenderData) {
745 self.gridcube_render_data = Some(data);
746 self.gridcube_dirty = false;
747 }
748
749 pub fn init_pick_resources(
753 &mut self,
754 device: &wgpu::Device,
755 pick_bind_group_layout: &wgpu::BindGroupLayout,
756 camera_buffer: &wgpu::Buffer,
757 global_start: u32,
758 ) {
759 self.global_start = global_start;
760
761 let Some(gridcube_rd) = &self.gridcube_render_data else {
762 return;
763 };
764
765 let uniforms = GridcubePickUniforms {
766 global_start,
767 cube_size_factor: 1.0,
768 ..Default::default()
769 };
770
771 let uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
772 label: Some("gridcube cell pick uniforms"),
773 contents: bytemuck::cast_slice(&[uniforms]),
774 usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
775 });
776
777 let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
778 label: Some("gridcube cell pick bind group"),
779 layout: pick_bind_group_layout,
780 entries: &[
781 wgpu::BindGroupEntry {
782 binding: 0,
783 resource: camera_buffer.as_entire_binding(),
784 },
785 wgpu::BindGroupEntry {
786 binding: 1,
787 resource: uniform_buffer.as_entire_binding(),
788 },
789 wgpu::BindGroupEntry {
790 binding: 2,
791 resource: gridcube_rd.position_buffer.as_entire_binding(),
792 },
793 ],
794 });
795
796 self.pick_uniform_buffer = Some(uniform_buffer);
797 self.pick_bind_group = Some(bind_group);
798 }
799
800 #[must_use]
802 pub fn pick_bind_group(&self) -> Option<&wgpu::BindGroup> {
803 self.pick_bind_group.as_ref()
804 }
805
806 pub fn update_pick_uniforms(
808 &self,
809 queue: &wgpu::Queue,
810 model: [[f32; 4]; 4],
811 cube_size_factor: f32,
812 ) {
813 if let Some(buffer) = &self.pick_uniform_buffer {
814 let uniforms = GridcubePickUniforms {
815 model,
816 global_start: self.global_start,
817 cube_size_factor,
818 ..Default::default()
819 };
820 queue.write_buffer(buffer, 0, bytemuck::cast_slice(&[uniforms]));
821 }
822 }
823
824 #[must_use]
826 pub fn num_pick_elements(&self) -> u32 {
827 self.gridcube_render_data
828 .as_ref()
829 .map_or(0, |rd| rd.num_instances)
830 }
831
832 #[must_use]
834 pub fn pick_total_vertices(&self) -> u32 {
835 self.gridcube_render_data
836 .as_ref()
837 .map_or(0, GridcubeRenderData::total_vertices)
838 }
839
840 pub fn build_egui_ui(&mut self, ui: &mut egui::Ui, colormap_names: &[&str]) {
842 ui.horizontal(|ui| {
843 let mut enabled = self.enabled;
844 if ui.checkbox(&mut enabled, "").changed() {
845 self.enabled = enabled;
846 }
847 ui.label(&self.name);
848 ui.label(format!("[{:.3}, {:.3}]", self.data_min, self.data_max));
849 });
850
851 if self.enabled {
852 let indent_id = egui::Id::new(&self.name).with("cell_scalar_indent");
853 ui.indent(indent_id, |ui| {
854 if !colormap_names.is_empty() {
856 ui.horizontal(|ui| {
857 ui.label("Colormap:");
858 egui::ComboBox::from_id_salt(format!("{}_colormap", self.name))
859 .selected_text(&self.color_map)
860 .show_ui(ui, |ui| {
861 for &name in colormap_names {
862 if ui.selectable_label(self.color_map == name, name).clicked() {
863 self.color_map = name.to_string();
864 self.gridcube_dirty = true;
865 }
866 }
867 });
868 });
869 }
870
871 ui.horizontal(|ui| {
873 ui.label("Range:");
874 let mut min = self.data_min;
875 let mut max = self.data_max;
876 let speed = (max - min).abs() * 0.01;
877 let speed = if speed > 0.0 { speed } else { 0.01 };
878 ui.add(egui::DragValue::new(&mut min).speed(speed));
879 ui.label("–");
880 ui.add(egui::DragValue::new(&mut max).speed(speed));
881 if (min - self.data_min).abs() > f32::EPSILON
882 || (max - self.data_max).abs() > f32::EPSILON
883 {
884 self.data_min = min;
885 self.data_max = max;
886 }
887 });
888 });
889 }
890 }
891}
892
893impl Quantity for VolumeGridCellScalarQuantity {
894 fn name(&self) -> &str {
895 &self.name
896 }
897
898 fn structure_name(&self) -> &str {
899 &self.structure_name
900 }
901
902 fn kind(&self) -> QuantityKind {
903 QuantityKind::Scalar
904 }
905
906 fn is_enabled(&self) -> bool {
907 self.enabled
908 }
909
910 fn set_enabled(&mut self, enabled: bool) {
911 self.enabled = enabled;
912 }
913
914 fn data_size(&self) -> usize {
915 self.values.len()
916 }
917
918 fn build_ui(&mut self, _ui: &dyn std::any::Any) {
919 }
921
922 fn refresh(&mut self) {
923 self.gridcube_render_data = None;
924 self.gridcube_dirty = true;
925 self.pick_uniform_buffer = None;
926 self.pick_bind_group = None;
927 }
928
929 fn clear_gpu_resources(&mut self) {
930 self.gridcube_render_data = None;
931 }
932
933 fn as_any(&self) -> &dyn std::any::Any {
934 self
935 }
936
937 fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
938 self
939 }
940}