Skip to main content

polyscope_structures/surface_mesh/
mod.rs

1//! Surface mesh structure.
2
3mod geometry;
4mod intrinsic_vector_quantity;
5mod one_form_quantity;
6mod parameterization_quantity;
7mod quantities;
8mod quantity_methods;
9pub use intrinsic_vector_quantity::*;
10pub use one_form_quantity::*;
11pub use parameterization_quantity::*;
12pub use quantities::*;
13
14use glam::{Mat4, Vec3, Vec4};
15use polyscope_core::pick::PickResult;
16use polyscope_core::quantity::Quantity;
17use polyscope_core::structure::{HasQuantities, RenderContext, Structure};
18use polyscope_render::{ColorMapRegistry, MeshPickUniforms, MeshUniforms, SurfaceMeshRenderData};
19use std::ops::Range;
20
21/// Shading style for surface mesh rendering.
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
23pub enum ShadeStyle {
24    /// Smooth shading using interpolated vertex normals.
25    #[default]
26    Smooth,
27    /// Flat shading using face normals.
28    Flat,
29    /// Flat shading per triangle (after triangulation).
30    TriFlat,
31}
32
33/// Policy for rendering backfaces of the mesh.
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
35pub enum BackfacePolicy {
36    /// Backfaces rendered identically to front faces.
37    #[default]
38    Identical,
39    /// Backfaces rendered with a different (darker) shade.
40    Different,
41    /// Backfaces rendered with a custom color.
42    Custom,
43    /// Backfaces are culled (not rendered).
44    Cull,
45}
46
47/// A surface mesh structure (triangular or polygonal).
48pub struct SurfaceMesh {
49    // Core data
50    name: String,
51    vertices: Vec<Vec3>,
52    faces: Vec<Vec<u32>>, // Variable-length polygons
53    enabled: bool,
54    transform: Mat4,
55    quantities: Vec<Box<dyn Quantity>>,
56
57    // Computed data
58    triangulation: Vec<[u32; 3]>,
59    face_to_tri_range: Vec<Range<usize>>,
60    vertex_normals: Vec<Vec3>,
61    face_normals: Vec<Vec3>,
62    corner_normals: Vec<Vec3>,
63    edge_is_real: Vec<Vec3>,
64    edges: Vec<(u32, u32)>,
65    needs_recompute: bool,
66
67    // Render options
68    material: String,
69    shade_style: ShadeStyle,
70    edge_width: f32,
71    edge_color: Vec4,
72    show_edges: bool,
73    backface_policy: BackfacePolicy,
74    backface_color: Vec4,
75    surface_color: Vec4,
76    transparency: f32,
77
78    // GPU resources
79    render_data: Option<SurfaceMeshRenderData>,
80
81    // GPU picking resources
82    pick_uniform_buffer: Option<wgpu::Buffer>,
83    pick_bind_group: Option<wgpu::BindGroup>,
84    pick_face_index_buffer: Option<wgpu::Buffer>,
85    global_start: u32,
86}
87
88impl SurfaceMesh {
89    /// Creates a new surface mesh from vertices and polygon faces.
90    ///
91    /// Each face is a variable-length list of vertex indices forming a polygon.
92    /// Triangles have 3 indices, quads have 4, etc.
93    pub fn new(name: impl Into<String>, vertices: Vec<Vec3>, faces: Vec<Vec<u32>>) -> Self {
94        let mut mesh = Self {
95            name: name.into(),
96            vertices,
97            faces,
98            enabled: true,
99            transform: Mat4::IDENTITY,
100            quantities: Vec::new(),
101
102            // Computed data (will be filled by recompute)
103            triangulation: Vec::new(),
104            face_to_tri_range: Vec::new(),
105            vertex_normals: Vec::new(),
106            face_normals: Vec::new(),
107            corner_normals: Vec::new(),
108            edge_is_real: Vec::new(),
109            edges: Vec::new(),
110            needs_recompute: true,
111
112            // Default render options
113            material: "clay".to_string(),
114            shade_style: ShadeStyle::default(),
115            edge_width: 1.0,
116            edge_color: Vec4::new(0.0, 0.0, 0.0, 1.0),
117            show_edges: false,
118            backface_policy: BackfacePolicy::default(),
119            backface_color: Vec4::new(0.3, 0.3, 0.3, 1.0),
120            surface_color: Vec4::new(0.5, 0.5, 0.8, 1.0),
121            transparency: 0.0, // 0.0 = fully opaque, 1.0 = fully transparent
122
123            render_data: None,
124
125            pick_uniform_buffer: None,
126            pick_bind_group: None,
127            pick_face_index_buffer: None,
128            global_start: 0,
129        };
130        mesh.recompute();
131        mesh
132    }
133
134    /// Creates a new surface mesh from triangles (convenience method).
135    ///
136    /// This is a convenience method for creating a mesh from triangle data.
137    pub fn from_triangles(
138        name: impl Into<String>,
139        vertices: Vec<Vec3>,
140        triangles: Vec<[u32; 3]>,
141    ) -> Self {
142        let faces: Vec<Vec<u32>> = triangles.into_iter().map(|t| t.to_vec()).collect();
143        Self::new(name, vertices, faces)
144    }
145
146    /// Returns the number of vertices.
147    #[must_use]
148    pub fn num_vertices(&self) -> usize {
149        self.vertices.len()
150    }
151
152    /// Returns the number of faces.
153    #[must_use]
154    pub fn num_faces(&self) -> usize {
155        self.faces.len()
156    }
157
158    /// Returns the number of triangles in the triangulation.
159    #[must_use]
160    pub fn num_triangles(&self) -> usize {
161        self.triangulation.len()
162    }
163
164    /// Returns the number of edges.
165    #[must_use]
166    pub fn num_edges(&self) -> usize {
167        self.edges.len()
168    }
169
170    /// Returns the vertices.
171    #[must_use]
172    pub fn vertices(&self) -> &[Vec3] {
173        &self.vertices
174    }
175
176    /// Returns the faces (polygon indices).
177    #[must_use]
178    pub fn faces(&self) -> &[Vec<u32>] {
179        &self.faces
180    }
181
182    /// Returns the triangulation.
183    #[must_use]
184    pub fn triangulation(&self) -> &[[u32; 3]] {
185        &self.triangulation
186    }
187
188    /// Returns the mapping from face index to triangle range.
189    #[must_use]
190    pub fn face_to_tri_range(&self) -> &[Range<usize>] {
191        &self.face_to_tri_range
192    }
193
194    /// Returns the vertex normals.
195    #[must_use]
196    pub fn vertex_normals(&self) -> &[Vec3] {
197        &self.vertex_normals
198    }
199
200    /// Returns the face normals.
201    #[must_use]
202    pub fn face_normals(&self) -> &[Vec3] {
203        &self.face_normals
204    }
205
206    /// Returns the corner normals (per triangle vertex).
207    #[must_use]
208    pub fn corner_normals(&self) -> &[Vec3] {
209        &self.corner_normals
210    }
211
212    /// Returns the `edge_is_real` flags for each triangle corner.
213    /// For each triangle vertex, this is a Vec3 where:
214    /// - x = 1.0 if edge from this vertex to next is real, 0.0 if internal
215    /// - y = 1.0 if edge from next to prev is real (always real for middle edge)
216    /// - z = 1.0 if edge from prev to this is real, 0.0 if internal
217    #[must_use]
218    pub fn edge_is_real(&self) -> &[Vec3] {
219        &self.edge_is_real
220    }
221
222    /// Returns the unique edges as sorted pairs.
223    #[must_use]
224    pub fn edges(&self) -> &[(u32, u32)] {
225        &self.edges
226    }
227
228    /// Updates the vertex positions.
229    pub fn update_vertices(&mut self, vertices: Vec<Vec3>) {
230        self.vertices = vertices;
231        self.needs_recompute = true;
232        self.refresh();
233    }
234
235    /// Updates the faces.
236    pub fn update_faces(&mut self, faces: Vec<Vec<u32>>) {
237        self.faces = faces;
238        self.needs_recompute = true;
239        self.refresh();
240    }
241
242    // === Render option getters and setters ===
243
244    /// Gets the shade style.
245    #[must_use]
246    pub fn shade_style(&self) -> ShadeStyle {
247        self.shade_style
248    }
249
250    /// Sets the shade style.
251    pub fn set_shade_style(&mut self, style: ShadeStyle) {
252        self.shade_style = style;
253    }
254
255    /// Gets the edge width.
256    #[must_use]
257    pub fn edge_width(&self) -> f32 {
258        self.edge_width
259    }
260
261    /// Sets the edge width.
262    pub fn set_edge_width(&mut self, width: f32) {
263        self.edge_width = width;
264    }
265
266    /// Gets the edge color.
267    #[must_use]
268    pub fn edge_color(&self) -> Vec4 {
269        self.edge_color
270    }
271
272    /// Sets the edge color.
273    pub fn set_edge_color(&mut self, color: Vec3) {
274        self.edge_color = color.extend(1.0);
275    }
276
277    /// Gets whether edges are shown.
278    #[must_use]
279    pub fn show_edges(&self) -> bool {
280        self.show_edges
281    }
282
283    /// Sets whether edges are shown.
284    pub fn set_show_edges(&mut self, show: bool) {
285        self.show_edges = show;
286    }
287
288    /// Gets the backface policy.
289    #[must_use]
290    pub fn backface_policy(&self) -> BackfacePolicy {
291        self.backface_policy
292    }
293
294    /// Sets the backface policy.
295    pub fn set_backface_policy(&mut self, policy: BackfacePolicy) {
296        self.backface_policy = policy;
297    }
298
299    /// Gets the backface color.
300    #[must_use]
301    pub fn backface_color(&self) -> Vec4 {
302        self.backface_color
303    }
304
305    /// Sets the backface color.
306    pub fn set_backface_color(&mut self, color: Vec3) {
307        self.backface_color = color.extend(1.0);
308    }
309
310    /// Gets the surface color.
311    #[must_use]
312    pub fn surface_color(&self) -> Vec4 {
313        self.surface_color
314    }
315
316    /// Sets the surface color.
317    pub fn set_surface_color(&mut self, color: Vec3) {
318        self.surface_color = color.extend(1.0);
319    }
320
321    /// Gets the transparency (0.0 = opaque, 1.0 = fully transparent).
322    #[must_use]
323    pub fn transparency(&self) -> f32 {
324        self.transparency
325    }
326
327    /// Sets the transparency.
328    pub fn set_transparency(&mut self, transparency: f32) {
329        self.transparency = transparency.clamp(0.0, 1.0);
330    }
331
332    /// Builds the egui UI for this surface mesh.
333    pub fn build_egui_ui(&mut self, ui: &mut egui::Ui, available_materials: &[&str]) {
334        let mut shade_style = self.shade_style as u32;
335        let mut color = [
336            self.surface_color.x,
337            self.surface_color.y,
338            self.surface_color.z,
339        ];
340        let mut transparency = self.transparency;
341        let mut show_edges = self.show_edges;
342        let mut edge_width = self.edge_width;
343        let mut edge_color = [self.edge_color.x, self.edge_color.y, self.edge_color.z];
344        let mut backface_policy = self.backface_policy as u32;
345
346        if polyscope_ui::build_surface_mesh_ui(
347            ui,
348            self.num_vertices(),
349            self.num_faces(),
350            self.num_edges(),
351            &mut shade_style,
352            &mut color,
353            &mut transparency,
354            &mut show_edges,
355            &mut edge_width,
356            &mut edge_color,
357            &mut backface_policy,
358            &mut self.material,
359            available_materials,
360        ) {
361            self.shade_style = match shade_style {
362                0 => ShadeStyle::Smooth,
363                1 => ShadeStyle::Flat,
364                _ => ShadeStyle::TriFlat,
365            };
366            self.surface_color = Vec4::new(color[0], color[1], color[2], self.surface_color.w);
367            self.transparency = transparency;
368            self.show_edges = show_edges;
369            self.edge_width = edge_width;
370            self.edge_color = Vec4::new(
371                edge_color[0],
372                edge_color[1],
373                edge_color[2],
374                self.edge_color.w,
375            );
376            self.backface_policy = match backface_policy {
377                0 => BackfacePolicy::Identical,
378                1 => BackfacePolicy::Different,
379                2 => BackfacePolicy::Custom,
380                _ => BackfacePolicy::Cull,
381            };
382        }
383
384        // Show quantities
385        if !self.quantities.is_empty() {
386            ui.separator();
387            ui.label("Quantities:");
388            for quantity in &mut self.quantities {
389                // Try downcasting to each known quantity type
390                if let Some(sq) = quantity
391                    .as_any_mut()
392                    .downcast_mut::<MeshVertexScalarQuantity>()
393                {
394                    sq.build_egui_ui(ui);
395                } else if let Some(sq) = quantity
396                    .as_any_mut()
397                    .downcast_mut::<MeshFaceScalarQuantity>()
398                {
399                    sq.build_egui_ui(ui);
400                } else if let Some(cq) = quantity
401                    .as_any_mut()
402                    .downcast_mut::<MeshVertexColorQuantity>()
403                {
404                    cq.build_egui_ui(ui);
405                } else if let Some(cq) = quantity
406                    .as_any_mut()
407                    .downcast_mut::<MeshFaceColorQuantity>()
408                {
409                    cq.build_egui_ui(ui);
410                } else if let Some(vq) = quantity
411                    .as_any_mut()
412                    .downcast_mut::<MeshVertexVectorQuantity>()
413                {
414                    vq.build_egui_ui(ui);
415                } else if let Some(vq) = quantity
416                    .as_any_mut()
417                    .downcast_mut::<MeshFaceVectorQuantity>()
418                {
419                    vq.build_egui_ui(ui);
420                } else if let Some(pq) = quantity
421                    .as_any_mut()
422                    .downcast_mut::<MeshVertexParameterizationQuantity>()
423                {
424                    pq.build_egui_ui(ui);
425                } else if let Some(pq) = quantity
426                    .as_any_mut()
427                    .downcast_mut::<MeshCornerParameterizationQuantity>()
428                {
429                    pq.build_egui_ui(ui);
430                } else if let Some(iq) = quantity
431                    .as_any_mut()
432                    .downcast_mut::<MeshVertexIntrinsicVectorQuantity>()
433                {
434                    iq.build_egui_ui(ui);
435                } else if let Some(iq) = quantity
436                    .as_any_mut()
437                    .downcast_mut::<MeshFaceIntrinsicVectorQuantity>()
438                {
439                    iq.build_egui_ui(ui);
440                } else if let Some(oq) = quantity.as_any_mut().downcast_mut::<MeshOneFormQuantity>()
441                {
442                    oq.build_egui_ui(ui);
443                }
444            }
445        }
446    }
447
448    // === GPU resource methods ===
449
450    /// Initializes GPU resources for rendering.
451    pub fn init_gpu_resources(
452        &mut self,
453        device: &wgpu::Device,
454        bind_group_layout: &wgpu::BindGroupLayout,
455        camera_buffer: &wgpu::Buffer,
456    ) {
457        self.render_data = Some(SurfaceMeshRenderData::new(
458            device,
459            bind_group_layout,
460            camera_buffer,
461            &self.vertices,
462            &self.triangulation,
463            &self.vertex_normals,
464            &self.edge_is_real,
465        ));
466    }
467
468    /// Returns the render data if initialized.
469    #[must_use]
470    pub fn render_data(&self) -> Option<&SurfaceMeshRenderData> {
471        self.render_data.as_ref()
472    }
473
474    /// Initializes shadow rendering resources.
475    ///
476    /// Creates the bind group needed to render this mesh in the shadow pass.
477    pub fn init_shadow_resources(
478        &mut self,
479        device: &wgpu::Device,
480        shadow_bind_group_layout: &wgpu::BindGroupLayout,
481        light_buffer: &wgpu::Buffer,
482    ) {
483        if let Some(render_data) = &mut self.render_data {
484            render_data.init_shadow_resources(device, shadow_bind_group_layout, light_buffer);
485        }
486    }
487
488    /// Returns the shadow bind group if initialized.
489    #[must_use]
490    pub fn shadow_bind_group(&self) -> Option<&wgpu::BindGroup> {
491        self.render_data.as_ref()?.shadow_bind_group.as_ref()
492    }
493
494    /// Returns whether shadow resources are initialized.
495    #[must_use]
496    pub fn has_shadow_resources(&self) -> bool {
497        self.render_data
498            .as_ref()
499            .is_some_and(polyscope_render::SurfaceMeshRenderData::has_shadow_resources)
500    }
501
502    /// Initializes GPU resources for pick rendering.
503    ///
504    /// Creates the pick uniform buffer, face index mapping buffer, and bind group.
505    /// The face index buffer maps each GPU triangle to its original polygon face index.
506    pub fn init_pick_resources(
507        &mut self,
508        device: &wgpu::Device,
509        mesh_pick_bind_group_layout: &wgpu::BindGroupLayout,
510        camera_buffer: &wgpu::Buffer,
511        global_start: u32,
512    ) {
513        use wgpu::util::DeviceExt;
514
515        self.global_start = global_start;
516
517        // Create pick uniform buffer with MeshPickUniforms
518        let model = self.transform.to_cols_array_2d();
519        let pick_uniforms = MeshPickUniforms {
520            global_start,
521            _padding: [0.0; 3],
522            model,
523        };
524        let pick_uniform_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
525            label: Some("mesh pick uniforms"),
526            contents: bytemuck::cast_slice(&[pick_uniforms]),
527            usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
528        });
529
530        // Build face index mapping buffer: tri_index -> face_index
531        // For each triangle in the triangulation, store which polygon face it belongs to
532        let mut face_index_data: Vec<u32> = Vec::with_capacity(self.triangulation.len());
533        for (face_idx, range) in self.face_to_tri_range.iter().enumerate() {
534            for _ in range.clone() {
535                face_index_data.push(face_idx as u32);
536            }
537        }
538        let pick_face_index_buffer = device.create_buffer_init(&wgpu::util::BufferInitDescriptor {
539            label: Some("mesh pick face indices"),
540            contents: bytemuck::cast_slice(&face_index_data),
541            usage: wgpu::BufferUsages::STORAGE,
542        });
543
544        // Create pick bind group (reuses position buffer from render_data)
545        if let Some(render_data) = &self.render_data {
546            let pick_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
547                label: Some("mesh pick bind group"),
548                layout: mesh_pick_bind_group_layout,
549                entries: &[
550                    wgpu::BindGroupEntry {
551                        binding: 0,
552                        resource: camera_buffer.as_entire_binding(),
553                    },
554                    wgpu::BindGroupEntry {
555                        binding: 1,
556                        resource: pick_uniform_buffer.as_entire_binding(),
557                    },
558                    wgpu::BindGroupEntry {
559                        binding: 2,
560                        resource: render_data.vertex_buffer.as_entire_binding(),
561                    },
562                    wgpu::BindGroupEntry {
563                        binding: 3,
564                        resource: pick_face_index_buffer.as_entire_binding(),
565                    },
566                ],
567            });
568            self.pick_bind_group = Some(pick_bind_group);
569        }
570
571        self.pick_uniform_buffer = Some(pick_uniform_buffer);
572        self.pick_face_index_buffer = Some(pick_face_index_buffer);
573    }
574
575    /// Returns the pick bind group if initialized.
576    #[must_use]
577    pub fn pick_bind_group(&self) -> Option<&wgpu::BindGroup> {
578        self.pick_bind_group.as_ref()
579    }
580
581    /// Updates pick uniforms (model transform) when the structure is moved.
582    pub fn update_pick_uniforms(&self, queue: &wgpu::Queue) {
583        if let Some(buffer) = &self.pick_uniform_buffer {
584            let model = self.transform.to_cols_array_2d();
585            let pick_uniforms = MeshPickUniforms {
586                global_start: self.global_start,
587                _padding: [0.0; 3],
588                model,
589            };
590            queue.write_buffer(buffer, 0, bytemuck::cast_slice(&[pick_uniforms]));
591        }
592    }
593
594    /// Returns the total number of vertices in the triangulation (for draw calls).
595    #[must_use]
596    pub fn num_triangulation_vertices(&self) -> u32 {
597        (self.triangulation.len() * 3) as u32
598    }
599
600    /// Updates GPU buffers with current mesh settings.
601    pub fn update_gpu_buffers(&self, queue: &wgpu::Queue, color_maps: &ColorMapRegistry) {
602        let Some(render_data) = &self.render_data else {
603            return;
604        };
605
606        // Convert glam Mat4 to [[f32; 4]; 4] for GPU
607        let model_matrix = self.transform.to_cols_array_2d();
608
609        let mut use_vertex_color = false;
610
611        // Apply quantity colors with priority:
612        // vertex param > corner param > vertex color > face color > vertex scalar > face scalar > surface color
613        if let Some(pq) = self.active_vertex_parameterization_quantity() {
614            use_vertex_color = true;
615            let colors = pq.compute_colors();
616            render_data.update_colors(queue, &colors, &self.triangulation);
617        } else if let Some(pq) = self.active_corner_parameterization_quantity() {
618            use_vertex_color = true;
619            // Corner parameterization: compute per-corner colors, expand to per-vertex
620            // (for now, treat as per-face by averaging corners)
621            let corner_colors = pq.compute_colors();
622            let mut vertex_colors = vec![Vec4::splat(0.5); self.vertices.len()];
623            let mut counts = vec![0u32; self.vertices.len()];
624            let mut corner_idx = 0;
625            for face in &self.faces {
626                for &vi in face {
627                    if corner_idx < corner_colors.len() {
628                        vertex_colors[vi as usize] += corner_colors[corner_idx];
629                        counts[vi as usize] += 1;
630                        corner_idx += 1;
631                    }
632                }
633            }
634            for (i, count) in counts.iter().enumerate() {
635                if *count > 0 {
636                    vertex_colors[i] /= *count as f32;
637                }
638            }
639            render_data.update_colors(queue, &vertex_colors, &self.triangulation);
640        } else if let Some(cq) = self.active_vertex_color_quantity() {
641            use_vertex_color = true;
642            // Direct vertex color quantity
643            render_data.update_colors(queue, cq.colors(), &self.triangulation);
644        } else if let Some(cq) = self.active_face_color_quantity() {
645            use_vertex_color = true;
646            // Face color expanded to vertices
647            let colors = cq.compute_vertex_colors(&self.faces, self.vertices.len());
648            render_data.update_colors(queue, &colors, &self.triangulation);
649        } else if let Some(sq) = self.active_vertex_scalar_quantity() {
650            use_vertex_color = true;
651            // Vertex scalar mapped through colormap
652            if let Some(colormap) = color_maps.get(sq.colormap_name()) {
653                let colors = sq.compute_colors(colormap);
654                render_data.update_colors(queue, &colors, &self.triangulation);
655            }
656        } else if let Some(sq) = self.active_face_scalar_quantity() {
657            use_vertex_color = true;
658            // Face scalar mapped through colormap and expanded to vertices
659            if let Some(colormap) = color_maps.get(sq.colormap_name()) {
660                let colors = sq.compute_vertex_colors(&self.faces, self.vertices.len(), colormap);
661                render_data.update_colors(queue, &colors, &self.triangulation);
662            }
663        } else {
664            // No quantity enabled - clear colors so shader uses surface_color
665            render_data.clear_colors(queue);
666        }
667
668        let uniforms = MeshUniforms {
669            model_matrix,
670            shade_style: self.shade_style as u32,
671            show_edges: u32::from(self.show_edges),
672            edge_width: self.edge_width,
673            transparency: self.transparency,
674            surface_color: self.surface_color.to_array(),
675            edge_color: self.edge_color.to_array(),
676            backface_policy: self.backface_policy as u32,
677            slice_planes_enabled: 1,
678            use_vertex_color: u32::from(use_vertex_color),
679            _pad1: 0.0,
680            _pad2: [0.0; 3],
681            _pad3: 0.0,
682            backface_color: self.backface_color.to_array(),
683        };
684        render_data.update_uniforms(queue, &uniforms);
685
686        // Update shadow model buffer if initialized
687        render_data.update_shadow_model(queue, model_matrix);
688    }
689}
690
691impl Structure for SurfaceMesh {
692    fn as_any(&self) -> &dyn std::any::Any {
693        self
694    }
695
696    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
697        self
698    }
699
700    fn name(&self) -> &str {
701        &self.name
702    }
703
704    fn type_name(&self) -> &'static str {
705        "SurfaceMesh"
706    }
707
708    fn bounding_box(&self) -> Option<(Vec3, Vec3)> {
709        if self.vertices.is_empty() {
710            return None;
711        }
712
713        let mut min = Vec3::splat(f32::MAX);
714        let mut max = Vec3::splat(f32::MIN);
715
716        for &v in &self.vertices {
717            min = min.min(v);
718            max = max.max(v);
719        }
720
721        // Apply transform (same logic as PointCloud)
722        let transform = self.transform;
723        let corners = [
724            transform.transform_point3(Vec3::new(min.x, min.y, min.z)),
725            transform.transform_point3(Vec3::new(max.x, min.y, min.z)),
726            transform.transform_point3(Vec3::new(min.x, max.y, min.z)),
727            transform.transform_point3(Vec3::new(max.x, max.y, min.z)),
728            transform.transform_point3(Vec3::new(min.x, min.y, max.z)),
729            transform.transform_point3(Vec3::new(max.x, min.y, max.z)),
730            transform.transform_point3(Vec3::new(min.x, max.y, max.z)),
731            transform.transform_point3(Vec3::new(max.x, max.y, max.z)),
732        ];
733
734        let mut world_min = Vec3::splat(f32::MAX);
735        let mut world_max = Vec3::splat(f32::MIN);
736        for corner in corners {
737            world_min = world_min.min(corner);
738            world_max = world_max.max(corner);
739        }
740
741        Some((world_min, world_max))
742    }
743
744    fn length_scale(&self) -> f32 {
745        self.bounding_box()
746            .map_or(1.0, |(min, max)| (max - min).length())
747    }
748
749    fn transform(&self) -> Mat4 {
750        self.transform
751    }
752
753    fn set_transform(&mut self, transform: Mat4) {
754        self.transform = transform;
755    }
756
757    fn is_enabled(&self) -> bool {
758        self.enabled
759    }
760
761    fn set_enabled(&mut self, enabled: bool) {
762        self.enabled = enabled;
763    }
764
765    fn material(&self) -> &str {
766        &self.material
767    }
768
769    fn set_material(&mut self, material: &str) {
770        self.material = material.to_string();
771    }
772
773    fn draw(&self, _ctx: &mut dyn RenderContext) {
774        // Rendering is handled by polyscope/src/app/render.rs
775    }
776
777    fn draw_pick(&self, _ctx: &mut dyn RenderContext) {
778        // Pick rendering is handled by polyscope/src/app/render.rs
779    }
780
781    fn build_ui(&mut self, _ui: &dyn std::any::Any) {
782        // UI is handled by polyscope-ui/src/structure_ui.rs
783    }
784
785    fn build_pick_ui(&self, _ui: &dyn std::any::Any, _pick: &PickResult) {
786        // Pick UI is handled by polyscope-ui/src/panels.rs
787    }
788
789    fn clear_gpu_resources(&mut self) {
790        self.render_data = None;
791        self.pick_uniform_buffer = None;
792        self.pick_bind_group = None;
793        self.pick_face_index_buffer = None;
794        for quantity in &mut self.quantities {
795            quantity.clear_gpu_resources();
796        }
797    }
798
799    fn refresh(&mut self) {
800        self.recompute();
801        for quantity in &mut self.quantities {
802            quantity.refresh();
803        }
804    }
805}
806
807impl HasQuantities for SurfaceMesh {
808    fn add_quantity(&mut self, quantity: Box<dyn Quantity>) {
809        self.quantities.push(quantity);
810    }
811
812    fn get_quantity(&self, name: &str) -> Option<&dyn Quantity> {
813        self.quantities
814            .iter()
815            .find(|q| q.name() == name)
816            .map(std::convert::AsRef::as_ref)
817    }
818
819    fn get_quantity_mut(&mut self, name: &str) -> Option<&mut Box<dyn Quantity>> {
820        self.quantities.iter_mut().find(|q| q.name() == name)
821    }
822
823    fn remove_quantity(&mut self, name: &str) -> Option<Box<dyn Quantity>> {
824        let idx = self.quantities.iter().position(|q| q.name() == name)?;
825        Some(self.quantities.remove(idx))
826    }
827
828    fn quantities(&self) -> &[Box<dyn Quantity>] {
829        &self.quantities
830    }
831}
832
833#[cfg(test)]
834mod tests {
835    use super::*;
836
837    /// Test triangle and quad mesh creation and triangulation.
838    #[test]
839    fn test_surface_mesh_creation_and_triangulation() {
840        // Triangle mesh
841        let vertices = vec![
842            Vec3::new(0.0, 0.0, 0.0),
843            Vec3::new(1.0, 0.0, 0.0),
844            Vec3::new(0.5, 1.0, 0.0),
845        ];
846        let mesh = SurfaceMesh::new("test_tri", vertices, vec![vec![0, 1, 2]]);
847        assert_eq!(mesh.num_vertices(), 3);
848        assert_eq!(mesh.num_faces(), 1);
849        assert_eq!(mesh.num_triangles(), 1);
850        assert_eq!(mesh.triangulation()[0], [0, 1, 2]);
851
852        // Quad mesh with fan triangulation
853        let vertices = vec![
854            Vec3::new(0.0, 0.0, 0.0),
855            Vec3::new(1.0, 0.0, 0.0),
856            Vec3::new(1.0, 1.0, 0.0),
857            Vec3::new(0.0, 1.0, 0.0),
858        ];
859        let mesh = SurfaceMesh::new("test_quad", vertices, vec![vec![0, 1, 2, 3]]);
860        assert_eq!(mesh.num_vertices(), 4);
861        assert_eq!(mesh.num_faces(), 1);
862        assert_eq!(mesh.num_triangles(), 2);
863        assert_eq!(mesh.triangulation()[0], [0, 1, 2]);
864        assert_eq!(mesh.triangulation()[1], [0, 2, 3]);
865        assert_eq!(mesh.face_to_tri_range()[0], 0..2);
866    }
867
868    /// Test face normal computation.
869    #[test]
870    fn test_face_normals() {
871        // Triangle in XY plane, CCW winding -> normal should point in +Z
872        let vertices = vec![
873            Vec3::new(0.0, 0.0, 0.0),
874            Vec3::new(1.0, 0.0, 0.0),
875            Vec3::new(0.0, 1.0, 0.0),
876        ];
877        let faces = vec![vec![0, 1, 2]];
878
879        let mesh = SurfaceMesh::new("test_normal", vertices, faces);
880
881        assert_eq!(mesh.face_normals().len(), 1);
882        let normal = mesh.face_normals()[0];
883        // Should point in +Z direction
884        assert!(
885            (normal.z - 1.0).abs() < 1e-6,
886            "Normal Z should be 1.0, got {}",
887            normal.z
888        );
889        assert!(
890            normal.x.abs() < 1e-6,
891            "Normal X should be 0.0, got {}",
892            normal.x
893        );
894        assert!(
895            normal.y.abs() < 1e-6,
896            "Normal Y should be 0.0, got {}",
897            normal.y
898        );
899    }
900
901    /// Test edge computation.
902    #[test]
903    fn test_edges() {
904        let vertices = vec![
905            Vec3::new(0.0, 0.0, 0.0),
906            Vec3::new(1.0, 0.0, 0.0),
907            Vec3::new(0.5, 1.0, 0.0),
908        ];
909        let faces = vec![vec![0, 1, 2]];
910
911        let mesh = SurfaceMesh::new("test_edges", vertices, faces);
912
913        // Triangle has 3 unique edges
914        assert_eq!(mesh.num_edges(), 3);
915
916        // Edges should be sorted pairs
917        let edges = mesh.edges();
918        assert!(edges.contains(&(0, 1)));
919        assert!(edges.contains(&(1, 2)));
920        assert!(edges.contains(&(0, 2)));
921    }
922
923    /// Test vertex normals computation.
924    #[test]
925    fn test_vertex_normals() {
926        // Single triangle in XY plane
927        let vertices = vec![
928            Vec3::new(0.0, 0.0, 0.0),
929            Vec3::new(1.0, 0.0, 0.0),
930            Vec3::new(0.0, 1.0, 0.0),
931        ];
932        let faces = vec![vec![0, 1, 2]];
933
934        let mesh = SurfaceMesh::new("test_vnormals", vertices, faces);
935
936        // All vertex normals should point in +Z for a single flat triangle
937        assert_eq!(mesh.vertex_normals().len(), 3);
938        for normal in mesh.vertex_normals() {
939            assert!(
940                (normal.z - 1.0).abs() < 1e-6,
941                "Vertex normal Z should be 1.0"
942            );
943        }
944    }
945
946    /// Test pentagon triangulation (5 vertices -> 3 triangles).
947    #[test]
948    fn test_pentagon_triangulation() {
949        let vertices = vec![
950            Vec3::new(0.0, 0.0, 0.0),
951            Vec3::new(1.0, 0.0, 0.0),
952            Vec3::new(1.5, 0.5, 0.0),
953            Vec3::new(0.75, 1.0, 0.0),
954            Vec3::new(-0.25, 0.5, 0.0),
955        ];
956        let faces = vec![vec![0, 1, 2, 3, 4]];
957
958        let mesh = SurfaceMesh::new("test_pentagon", vertices, faces);
959
960        // Pentagon -> 3 triangles via fan triangulation
961        assert_eq!(mesh.num_triangles(), 3);
962        assert_eq!(mesh.triangulation()[0], [0, 1, 2]);
963        assert_eq!(mesh.triangulation()[1], [0, 2, 3]);
964        assert_eq!(mesh.triangulation()[2], [0, 3, 4]);
965
966        // Pentagon has 5 edges
967        assert_eq!(mesh.num_edges(), 5);
968    }
969
970    /// Test shared edge between two triangles.
971    #[test]
972    fn test_shared_edges() {
973        // Two triangles sharing an edge (0-1)
974        let vertices = vec![
975            Vec3::new(0.0, 0.0, 0.0),
976            Vec3::new(1.0, 0.0, 0.0),
977            Vec3::new(0.5, 1.0, 0.0),
978            Vec3::new(0.5, -1.0, 0.0),
979        ];
980        let faces = vec![vec![0, 1, 2], vec![0, 3, 1]];
981
982        let mesh = SurfaceMesh::new("test_shared", vertices, faces);
983
984        // 2 triangles, but only 5 unique edges (edge 0-1 is shared)
985        assert_eq!(mesh.num_triangles(), 2);
986        assert_eq!(mesh.num_edges(), 5);
987    }
988
989    /// Test render options getters and setters.
990    #[test]
991    fn test_render_options() {
992        let vertices = vec![
993            Vec3::new(0.0, 0.0, 0.0),
994            Vec3::new(1.0, 0.0, 0.0),
995            Vec3::new(0.5, 1.0, 0.0),
996        ];
997        let faces = vec![vec![0, 1, 2]];
998
999        let mut mesh = SurfaceMesh::new("test_options", vertices, faces);
1000
1001        // Test defaults
1002        assert_eq!(mesh.shade_style(), ShadeStyle::Smooth);
1003        assert_eq!(mesh.backface_policy(), BackfacePolicy::Identical);
1004        assert!(!mesh.show_edges());
1005        assert_eq!(mesh.transparency(), 0.0); // 0.0 = fully opaque
1006
1007        // Test setters
1008        mesh.set_shade_style(ShadeStyle::Flat);
1009        assert_eq!(mesh.shade_style(), ShadeStyle::Flat);
1010
1011        mesh.set_backface_policy(BackfacePolicy::Cull);
1012        assert_eq!(mesh.backface_policy(), BackfacePolicy::Cull);
1013
1014        mesh.set_show_edges(true);
1015        assert!(mesh.show_edges());
1016
1017        mesh.set_edge_width(2.0);
1018        assert_eq!(mesh.edge_width(), 2.0);
1019
1020        mesh.set_edge_color(Vec3::new(1.0, 0.0, 0.0));
1021        assert_eq!(mesh.edge_color(), Vec4::new(1.0, 0.0, 0.0, 1.0));
1022
1023        mesh.set_surface_color(Vec3::new(0.0, 1.0, 0.0));
1024        assert_eq!(mesh.surface_color(), Vec4::new(0.0, 1.0, 0.0, 1.0));
1025
1026        mesh.set_backface_color(Vec3::new(0.0, 0.0, 1.0));
1027        assert_eq!(mesh.backface_color(), Vec4::new(0.0, 0.0, 1.0, 1.0));
1028
1029        mesh.set_transparency(0.5);
1030        assert_eq!(mesh.transparency(), 0.5);
1031
1032        // Test transparency clamping
1033        mesh.set_transparency(1.5);
1034        assert_eq!(mesh.transparency(), 1.0);
1035        mesh.set_transparency(-0.5);
1036        assert_eq!(mesh.transparency(), 0.0);
1037    }
1038
1039    /// Test all quantity types on a surface mesh.
1040    #[test]
1041    fn test_surface_mesh_quantities() {
1042        use polyscope_core::quantity::QuantityKind;
1043
1044        let vertices = vec![
1045            Vec3::new(0.0, 0.0, 0.0),
1046            Vec3::new(1.0, 0.0, 0.0),
1047            Vec3::new(0.0, 1.0, 0.0),
1048        ];
1049        let faces = vec![vec![0, 1, 2]];
1050        let mut mesh = SurfaceMesh::new("test", vertices, faces);
1051
1052        // Vertex quantities (size = 3)
1053        mesh.add_vertex_scalar_quantity("height", vec![0.0, 0.5, 1.0]);
1054        mesh.add_vertex_color_quantity("colors", vec![Vec3::X, Vec3::Y, Vec3::Z]);
1055        mesh.add_vertex_vector_quantity("normals", vec![Vec3::Z, Vec3::Z, Vec3::Z]);
1056
1057        // Face quantities (size = 1)
1058        mesh.add_face_scalar_quantity("area", vec![1.0]);
1059        mesh.add_face_color_quantity("face_colors", vec![Vec3::new(1.0, 0.0, 0.0)]);
1060        mesh.add_face_vector_quantity("face_normals", vec![Vec3::Z]);
1061
1062        let cases: &[(&str, usize, QuantityKind)] = &[
1063            ("height", 3, QuantityKind::Scalar),
1064            ("colors", 3, QuantityKind::Color),
1065            ("normals", 3, QuantityKind::Vector),
1066            ("area", 1, QuantityKind::Scalar),
1067            ("face_colors", 1, QuantityKind::Color),
1068            ("face_normals", 1, QuantityKind::Vector),
1069        ];
1070
1071        for (name, expected_size, expected_kind) in cases {
1072            let q = mesh
1073                .get_quantity(name)
1074                .unwrap_or_else(|| panic!("quantity '{name}' not found"));
1075            assert_eq!(
1076                q.data_size(),
1077                *expected_size,
1078                "data_size mismatch for {name}"
1079            );
1080            assert_eq!(q.kind(), *expected_kind, "kind mismatch for {name}");
1081        }
1082    }
1083
1084    /// Test face scalar quantity compute_vertex_colors.
1085    #[test]
1086    fn test_face_scalar_compute_vertex_colors() {
1087        let fsq = MeshFaceScalarQuantity::new("test", "mesh", vec![0.0, 1.0]);
1088        let faces = vec![vec![0, 1, 2], vec![2, 1, 3]];
1089        let colormap = polyscope_render::ColorMap::new("test", vec![Vec3::ZERO, Vec3::ONE]);
1090
1091        let colors = fsq.compute_vertex_colors(&faces, 4, &colormap);
1092
1093        // Each vertex gets color from the last face it belongs to
1094        assert_eq!(colors.len(), 4);
1095        // Vertex 0 is only in face 0 (value 0.0) -> should be near zero RGB
1096        assert!((colors[0] - Vec4::new(0.0, 0.0, 0.0, 1.0)).length() < 1e-5);
1097        // Vertex 3 is only in face 1 (value 1.0) -> should be near one RGB
1098        assert!((colors[3] - Vec4::new(1.0, 1.0, 1.0, 1.0)).length() < 1e-5);
1099    }
1100
1101    /// Test face color quantity compute_vertex_colors.
1102    #[test]
1103    fn test_face_color_compute_vertex_colors() {
1104        let fcq = MeshFaceColorQuantity::new(
1105            "test",
1106            "mesh",
1107            vec![Vec3::new(1.0, 0.0, 0.0), Vec3::new(0.0, 1.0, 0.0)],
1108        );
1109        let faces = vec![vec![0, 1, 2], vec![2, 1, 3]];
1110
1111        let colors = fcq.compute_vertex_colors(&faces, 4);
1112
1113        assert_eq!(colors.len(), 4);
1114        // Vertex 0 is only in face 0 -> red
1115        assert_eq!(colors[0], Vec4::new(1.0, 0.0, 0.0, 1.0));
1116        // Vertex 3 is only in face 1 -> green
1117        assert_eq!(colors[3], Vec4::new(0.0, 1.0, 0.0, 1.0));
1118    }
1119
1120    /// Test edge_is_real for triangle (all edges should be real).
1121    #[test]
1122    fn test_edge_is_real_triangle() {
1123        let vertices = vec![
1124            Vec3::new(0.0, 0.0, 0.0),
1125            Vec3::new(1.0, 0.0, 0.0),
1126            Vec3::new(0.5, 1.0, 0.0),
1127        ];
1128        let faces = vec![vec![0, 1, 2]];
1129
1130        let mesh = SurfaceMesh::new("test_tri", vertices, faces);
1131
1132        // Triangle has 1 triangle, so 3 edge_is_real entries (one per corner)
1133        assert_eq!(mesh.edge_is_real().len(), 3);
1134
1135        // For a single triangle (1 tri from fan), all edges are real
1136        // j=0, num_tris=1: edge0_real=1.0, edge1_real=1.0, edge2_real=1.0
1137        let expected = Vec3::new(1.0, 1.0, 1.0);
1138        for edge_real in mesh.edge_is_real() {
1139            assert_eq!(*edge_real, expected);
1140        }
1141    }
1142
1143    /// Test edge_is_real for quad (internal edge should not be real).
1144    #[test]
1145    fn test_edge_is_real_quad() {
1146        let vertices = vec![
1147            Vec3::new(0.0, 0.0, 0.0),
1148            Vec3::new(1.0, 0.0, 0.0),
1149            Vec3::new(1.0, 1.0, 0.0),
1150            Vec3::new(0.0, 1.0, 0.0),
1151        ];
1152        let faces = vec![vec![0, 1, 2, 3]];
1153
1154        let mesh = SurfaceMesh::new("test_quad", vertices, faces);
1155
1156        // Quad has 2 triangles, so 6 edge_is_real entries
1157        assert_eq!(mesh.edge_is_real().len(), 6);
1158
1159        // Fan triangulation: [0,1,2], [0,2,3]
1160        // Triangle 0 (j=0, num_tris=2):
1161        //   edge0 (0->1): real (j==0)
1162        //   edge1 (1->2): always real
1163        //   edge2 (2->0): NOT real (j != num_tris-1)
1164        let tri0_expected = Vec3::new(1.0, 1.0, 0.0);
1165
1166        // Triangle 1 (j=1, num_tris=2):
1167        //   edge0 (0->2): NOT real (j!=0)
1168        //   edge1 (2->3): always real
1169        //   edge2 (3->0): real (j == num_tris-1)
1170        let tri1_expected = Vec3::new(0.0, 1.0, 1.0);
1171
1172        // First 3 entries are for triangle 0
1173        assert_eq!(mesh.edge_is_real()[0], tri0_expected);
1174        assert_eq!(mesh.edge_is_real()[1], tri0_expected);
1175        assert_eq!(mesh.edge_is_real()[2], tri0_expected);
1176
1177        // Next 3 entries are for triangle 1
1178        assert_eq!(mesh.edge_is_real()[3], tri1_expected);
1179        assert_eq!(mesh.edge_is_real()[4], tri1_expected);
1180        assert_eq!(mesh.edge_is_real()[5], tri1_expected);
1181    }
1182
1183    /// Test edge_is_real for pentagon (multiple internal edges).
1184    #[test]
1185    fn test_edge_is_real_pentagon() {
1186        let vertices = vec![
1187            Vec3::new(0.0, 0.0, 0.0),
1188            Vec3::new(1.0, 0.0, 0.0),
1189            Vec3::new(1.5, 0.5, 0.0),
1190            Vec3::new(0.75, 1.0, 0.0),
1191            Vec3::new(-0.25, 0.5, 0.0),
1192        ];
1193        let faces = vec![vec![0, 1, 2, 3, 4]];
1194
1195        let mesh = SurfaceMesh::new("test_pentagon", vertices, faces);
1196
1197        // Pentagon has 3 triangles, so 9 edge_is_real entries
1198        assert_eq!(mesh.edge_is_real().len(), 9);
1199
1200        // Fan triangulation: [0,1,2], [0,2,3], [0,3,4]
1201        // Triangle 0 (j=0, num_tris=3): (1.0, 1.0, 0.0)
1202        // Triangle 1 (j=1, num_tris=3): (0.0, 1.0, 0.0) - middle triangle, only edge1 real
1203        // Triangle 2 (j=2, num_tris=3): (0.0, 1.0, 1.0)
1204
1205        let tri0_expected = Vec3::new(1.0, 1.0, 0.0);
1206        let tri1_expected = Vec3::new(0.0, 1.0, 0.0);
1207        let tri2_expected = Vec3::new(0.0, 1.0, 1.0);
1208
1209        // Check triangle 0 corners
1210        assert_eq!(mesh.edge_is_real()[0], tri0_expected);
1211        assert_eq!(mesh.edge_is_real()[1], tri0_expected);
1212        assert_eq!(mesh.edge_is_real()[2], tri0_expected);
1213
1214        // Check triangle 1 corners
1215        assert_eq!(mesh.edge_is_real()[3], tri1_expected);
1216        assert_eq!(mesh.edge_is_real()[4], tri1_expected);
1217        assert_eq!(mesh.edge_is_real()[5], tri1_expected);
1218
1219        // Check triangle 2 corners
1220        assert_eq!(mesh.edge_is_real()[6], tri2_expected);
1221        assert_eq!(mesh.edge_is_real()[7], tri2_expected);
1222        assert_eq!(mesh.edge_is_real()[8], tri2_expected);
1223    }
1224}