fenris_geometry/
polymesh.rs

1use fenris_traits::Real;
2use itertools::Itertools;
3use nalgebra::allocator::Allocator;
4use nalgebra::{DefaultAllocator, DimName, OPoint, Point3, Scalar, Vector3, U3};
5use numeric_literals::replace_float_literals;
6use serde::{Deserialize, Serialize};
7use std::cmp::{max, min};
8use std::collections::{BTreeMap, BTreeSet, HashMap};
9use std::error::Error;
10use std::fmt;
11use std::fmt::Display;
12use std::hash::Hash;
13
14// use crate::connectivity::Connectivity;
15use crate::{compute_polyhedron_volume_from_faces, ConvexPolygon3d, ConvexPolyhedron, HalfSpace, LineSegment3d};
16// use crate::mesh::Mesh;
17use fenris_nested_vec::NestedVec;
18
19#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
20struct UndirectedEdge {
21    // Indices are always sorted, so that a <= b, for [a, b]
22    indices: [usize; 2],
23}
24
25impl UndirectedEdge {
26    pub fn new(a: usize, b: usize) -> Self {
27        Self {
28            indices: [min(a, b), max(a, b)],
29        }
30    }
31
32    pub fn indices(&self) -> &[usize; 2] {
33        &self.indices
34    }
35}
36
37#[derive(Debug)]
38pub struct PolyMeshFace<'a, T: Scalar, D: DimName>
39where
40    DefaultAllocator: Allocator<T, D>,
41{
42    all_vertices: &'a [OPoint<T, D>],
43    face_vertex_indices: &'a [usize],
44}
45
46impl<'a, T: Scalar> ConvexPolygon3d<'a, T> for PolyMeshFace<'a, T, U3> {
47    fn num_vertices(&self) -> usize {
48        self.face_vertex_indices.len()
49    }
50
51    fn get_vertex(&self, index: usize) -> Option<OPoint<T, U3>> {
52        let v = self
53            .all_vertices
54            .get(*self.face_vertex_indices.get(index)?)
55            .expect("Internal error: Vertex must always exist if the local index is valid.");
56        Some(v.clone())
57    }
58}
59
60/// A volumetric polytopal mesh.
61///
62/// It is assumed that each polytopal cell is convex.
63#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
64#[serde(bound(serialize = "OPoint<T, D>: Serialize"))]
65#[serde(bound(deserialize = "OPoint<T, D>: Deserialize<'de>"))]
66pub struct PolyMesh<T, D>
67where
68    T: Scalar,
69    D: DimName,
70    DefaultAllocator: Allocator<T, D>,
71{
72    #[serde(bound(serialize = "<DefaultAllocator as Allocator<T, D>>::Buffer: Serialize"))]
73    #[serde(bound(deserialize = "<DefaultAllocator as Allocator<T, D>>::Buffer: Deserialize<'de>"))]
74    vertices: Vec<OPoint<T, D>>,
75    faces: NestedVec<usize>,
76    cells: NestedVec<usize>,
77}
78
79pub type PolyMesh3d<T> = PolyMesh<T, U3>;
80
81impl<T, D> PolyMesh<T, D>
82where
83    T: Scalar,
84    D: DimName,
85    DefaultAllocator: Allocator<T, D>,
86{
87    /// Creates an empty poly mesh without any vertices, faces or cells.
88    pub fn new_empty() -> Self {
89        Self::from_poly_data(vec![], NestedVec::new(), NestedVec::new())
90    }
91
92    pub fn from_poly_data(vertices: Vec<OPoint<T, D>>, faces: NestedVec<usize>, cells: NestedVec<usize>) -> Self {
93        let num_vertices = vertices.len();
94        let num_faces = faces.len();
95
96        if faces.iter_array_elements().any(|idx| *idx >= num_vertices) {
97            panic!("Vertex index out of bounds in faces description.")
98        }
99
100        if cells.iter_array_elements().any(|idx| *idx >= num_faces) {
101            panic!("Face index out of bounds in cells description.")
102        }
103
104        Self {
105            vertices,
106            faces: faces,
107            cells: cells,
108        }
109    }
110
111    pub fn vertices(&self) -> &[OPoint<T, D>] {
112        &self.vertices
113    }
114
115    pub fn vertices_mut(&mut self) -> &mut [OPoint<T, D>] {
116        &mut self.vertices
117    }
118
119    pub fn num_faces(&self) -> usize {
120        self.faces.len()
121    }
122
123    pub fn num_cells(&self) -> usize {
124        self.cells.len()
125    }
126
127    pub fn face_vertices<'a>(&'a self, face_idx: usize) -> impl 'a + Iterator<Item = &'a OPoint<T, D>> {
128        self.get_face_connectivity(face_idx)
129            .into_iter()
130            .flatten()
131            .map(move |vertex_idx| &self.vertices()[*vertex_idx])
132    }
133
134    pub fn face_connectivity_iter<'a>(&'a self) -> impl 'a + Iterator<Item = &'a [usize]> {
135        self.faces.iter()
136    }
137
138    pub fn cell_connectivity_iter<'a>(&'a self) -> impl 'a + Iterator<Item = &'a [usize]> {
139        self.cells.iter()
140    }
141
142    pub fn get_face_connectivity(&self, index: usize) -> Option<&[usize]> {
143        self.faces.get(index)
144    }
145
146    pub fn get_cell_connectivity(&self, index: usize) -> Option<&[usize]> {
147        self.cells.get(index)
148    }
149
150    pub fn get_face(&self, index: usize) -> Option<PolyMeshFace<T, D>> {
151        self.get_face_connectivity(index)
152            .map(|face_vertex_indices| PolyMeshFace {
153                all_vertices: &self.vertices,
154                face_vertex_indices,
155            })
156    }
157
158    /// Returns a nested array, in which each array i contains the indices of the cells
159    /// associated with face i.
160    pub fn compute_face_cell_connectivity(&self) -> NestedVec<usize> {
161        // TODO: Implement this more efficiently so that we don't construct a
162        // Vec<Vec<_>>. Ideally we'd implement facilities for CompactArrayStorage to be able
163        // to push directly to existing arrays (by moving the array around in storage if necessary).
164        let mut connectivity = vec![Vec::new(); self.num_faces()];
165
166        for (cell_idx, cell) in self.cell_connectivity_iter().enumerate() {
167            for face_idx in cell {
168                connectivity[*face_idx].push(cell_idx);
169            }
170        }
171
172        let mut compact = NestedVec::new();
173        for face_cell_conn in connectivity {
174            compact.push(&face_cell_conn);
175        }
176
177        compact
178    }
179
180    /// Removes duplicate instances of topologically equivalent faces.
181    ///
182    /// Two faces are topologically equivalent if the sets of vertices connected by each face
183    /// are equivalent.
184    pub fn dedup_faces(&mut self) {
185        let mut sorted_face_connectivity = NestedVec::new();
186        for face in self.face_connectivity_iter() {
187            sorted_face_connectivity.push(face);
188            sorted_face_connectivity.last_mut().unwrap().sort_unstable();
189        }
190
191        let mut new_faces = NestedVec::new();
192        let mut connectivity_map = HashMap::new();
193        // Store new_indices of faces
194        let mut face_relabel = Vec::with_capacity(self.num_faces());
195        for i in 0..self.num_faces() {
196            let sorted_face_conn = sorted_face_connectivity.get(i).unwrap();
197            // TODO: Rewrite using entry API to avoid double lookup
198            if let Some(new_face_index) = connectivity_map.get(sorted_face_conn) {
199                // We've already encountered a face with equivalent topology,
200                // so map this face to the one we've found already
201                face_relabel.push(*new_face_index);
202            } else {
203                // We haven't so far encountered a face with equivalent topology,
204                // so we add this face to the new collection of faces.
205                let new_index = new_faces.len();
206                connectivity_map.insert(sorted_face_conn, new_index);
207                new_faces.push(self.get_face_connectivity(i).unwrap());
208                face_relabel.push(new_index);
209            }
210        }
211
212        self.faces = new_faces;
213
214        for i in 0..self.num_cells() {
215            let cell = self.cells.get_mut(i).unwrap();
216
217            for face_idx in cell {
218                *face_idx = face_relabel[*face_idx];
219            }
220        }
221    }
222
223    /// Returns the indices of the faces which are only referenced by at most one cells.
224    pub fn find_boundary_faces(&self) -> Vec<usize> {
225        let mut face_occurences = vec![0; self.num_faces()];
226
227        for cell_faces in self.cell_connectivity_iter() {
228            for face in cell_faces {
229                face_occurences[*face] += 1;
230            }
231        }
232
233        face_occurences
234            .into_iter()
235            .enumerate()
236            .filter_map(|(face_idx, count)| if count <= 1 { Some(face_idx) } else { None })
237            .collect()
238    }
239
240    /// Merges multiple meshes into a single instance of `PolyMesh`.
241    ///
242    /// The mesh vertices, faces and cells are simply relabeled and glued together so that they
243    /// form a well-defined PolyMesh. No mesh processing is performed.
244    pub fn concatenate<'a>(meshes: impl IntoIterator<Item = &'a PolyMesh<T, D>>) -> Self {
245        let meshes = meshes.into_iter();
246        let mut vertices = Vec::new();
247        let mut faces = NestedVec::new();
248        let mut cells = NestedVec::new();
249
250        let mut vertex_offset = 0;
251        let mut face_offset = 0;
252
253        for mesh in meshes {
254            vertices.extend(mesh.vertices().iter().cloned());
255
256            for face_vertices in mesh.face_connectivity_iter() {
257                let mut new_face_vertices = faces.begin_array();
258                for vertex_idx in face_vertices {
259                    new_face_vertices.push_single(vertex_idx + vertex_offset);
260                }
261            }
262
263            for cell_faces in mesh.cell_connectivity_iter() {
264                let mut new_cell_faces = cells.begin_array();
265                for face_idx in cell_faces {
266                    new_cell_faces.push_single(face_idx + face_offset);
267                }
268            }
269
270            vertex_offset += mesh.vertices().len();
271            face_offset += mesh.num_faces();
272        }
273
274        Self::from_poly_data(vertices, faces, cells)
275    }
276}
277
278impl<T, D> PolyMesh<T, D>
279where
280    T: Real,
281    D: DimName,
282    DefaultAllocator: Allocator<T, D>,
283{
284    /// Recursively splits each edge in the mesh the specified number of times
285    pub fn split_edges_n_times(&mut self, n_times: usize) {
286        for _ in 0..n_times {
287            self.split_edges()
288        }
289    }
290
291    /// Splits the edges of all faces in the mesh by inserting a vertex at the midpoint of each edge
292    #[replace_float_literals(T::from_f64(literal).unwrap())]
293    pub fn split_edges(&mut self) {
294        let vertex_offset = self.vertices.len();
295        let mut additional_vertices = Vec::new();
296        let mut new_faces = NestedVec::new();
297        let mut subdivided_edges = HashMap::new();
298
299        for face in self.faces.iter() {
300            let edges = face
301                .iter()
302                .copied()
303                .cycle()
304                .take(face.len() + 1)
305                .tuple_windows();
306            let mut new_face = new_faces.begin_array();
307            for (v1, v2) in edges {
308                let edge = [v1.min(v2), v1.max(v2)];
309                let v12 = *subdivided_edges.entry(edge).or_insert_with(|| {
310                    let new_vertex_index = vertex_offset + additional_vertices.len();
311                    let midpoint = OPoint::from((&self.vertices[v1].coords + &self.vertices[v2].coords) * 0.5);
312                    additional_vertices.push(midpoint);
313                    new_vertex_index
314                });
315                new_face.push_single(v1);
316                new_face.push_single(v12);
317            }
318        }
319
320        self.vertices.extend(additional_vertices);
321        self.faces = new_faces;
322    }
323}
324
325impl<T> PolyMesh3d<T>
326where
327    T: Scalar,
328{
329    /// Triangulate the polyhedral mesh.
330    ///
331    /// Note that the algorithm currently only gives non-degenerate results when each cell
332    /// is *strictly* convex, in the sense that no two faces of a cell are co-planar.
333    ///
334    /// TODO: Can we relax the strict convexity restriction without introducing additional
335    /// Steiner points into the triangulation? The restriction is explained in the
336    /// paper by Max (2000) (see comments in implementation).
337    pub fn triangulate(&self) -> Result<PolyMesh3d<T>, Box<dyn Error>> {
338        // The implementation here follows the procedure described in
339        // Nelson Max (2000), "Consistent Subdivision of Convex Polyhedra into Tetrahedra"
340        // The key is as follows: Whenever subdividing a face/cell into triangles/tetrahedra,
341        // we do so by connecting the vertex of smallest index to the triangle faces
342        // that were obtained from faces not incident to the vertex.
343
344        // First triangulate each face and maintain a map from the original face to the
345        // new face indices
346
347        let mut triangulated_faces = NestedVec::new();
348        let mut face_map = NestedVec::new();
349
350        for face in self.face_connectivity_iter() {
351            if face.len() < 3 {
352                return Err(Box::from(
353                    "Encountered face with less than 3 vertices,\
354                     cannot triangulate.",
355                ));
356            }
357
358            let mut face_map_array = face_map.begin_array();
359            // Pick the vertex with smallest index
360            let (min_idx, _) = face
361                .iter()
362                .enumerate()
363                .min_by_key(|(_, v_idx)| *v_idx)
364                .unwrap();
365
366            for i in 0..face.len() - 2 {
367                let a = face[min_idx];
368                let b = face[(i + 1 + min_idx) % face.len()];
369                let c = face[(i + 2 + min_idx) % face.len()];
370                face_map_array.push_single(triangulated_faces.len());
371                triangulated_faces.push(&[a, b, c]);
372            }
373        }
374
375        let mut tetrahedral_cells = NestedVec::new();
376        for cell in self.cell_connectivity_iter() {
377            // Ignore empty cells
378            if cell.len() > 0 {
379                // Pick the vertex of least index in the cell
380                let v_idx = cell
381                    .iter()
382                    .flat_map(|face_idx| {
383                        self.get_face_connectivity(*face_idx).expect(
384                            "Logic error: Cell references face that \
385                             does not exist.",
386                        )
387                    })
388                    .min()
389                    .expect("Cell is non-empty");
390
391                // For each original face in the cell that does *not* contain the vertex,
392                // create new tetrahedral cells by connecting the chosen vertex
393                // to the triangulated faces.
394                // It is important to discard the *original* faces with this test, because
395                // otherwise we would create degenerate tetrahedral cells, as our
396                // chosen vertex would be in the same plane as the 3 vertices of the triangulated
397                // face.
398                for original_face_idx in cell {
399                    let original_face_vertices = self
400                        .get_face_connectivity(*original_face_idx)
401                        .expect("Logic error: Cell references face that does not exist.");
402
403                    if !original_face_vertices.contains(v_idx) {
404                        let triangulated_face_indices = face_map
405                            .get(*original_face_idx)
406                            .expect("Logic error: Cell references face that does not exist.");
407
408                        for tri_face_idx in triangulated_face_indices {
409                            let tri_face_vertices = triangulated_faces.get(*tri_face_idx).unwrap();
410                            assert_eq!(tri_face_vertices.len(), 3);
411
412                            // Connect v to face by constructing 3 new triangular faces
413                            let a = tri_face_vertices[0];
414                            let b = tri_face_vertices[1];
415                            let c = tri_face_vertices[2];
416                            let v = *v_idx;
417
418                            // Triangular faces denoted by vertices connected,
419                            // i.e. abc means triangular face constructed by vertices a, b and c.
420                            let abc_idx = *tri_face_idx;
421                            let abv_idx = triangulated_faces.len();
422                            let bcv_idx = abv_idx + 1;
423                            let cav_idx = bcv_idx + 1;
424
425                            // This will cause duplicated faces, but we deduplicate them
426                            // as a post-process. TODO: Can we directly construct
427                            // faces without duplicating faces in a succinct way?
428                            triangulated_faces.push(&[a, b, v]);
429                            triangulated_faces.push(&[b, c, v]);
430                            triangulated_faces.push(&[c, a, v]);
431                            tetrahedral_cells.push(&[abc_idx, abv_idx, bcv_idx, cav_idx]);
432                        }
433                    }
434                }
435            }
436        }
437
438        let mut new_poly_mesh =
439            PolyMesh::from_poly_data(self.vertices().to_vec(), triangulated_faces, tetrahedral_cells);
440        new_poly_mesh.dedup_faces();
441        Ok(new_poly_mesh)
442    }
443
444    pub fn keep_cells(&self, cell_indices: &[usize]) -> Self {
445        // Use BTreeSets so that the relative order of the indices are kept
446        let keep_faces: BTreeSet<_> = cell_indices
447            .iter()
448            .flat_map(|cell_idx| {
449                self.get_cell_connectivity(*cell_idx)
450                    .expect("All cell indices must be in bounds")
451            })
452            .copied()
453            .collect();
454
455        let keep_vertices: BTreeSet<_> = keep_faces
456            .iter()
457            .flat_map(|face_idx| self.get_face_connectivity(*face_idx).unwrap())
458            .copied()
459            .collect();
460
461        let faces_old_to_new_map: HashMap<_, _> = keep_faces
462            .iter()
463            .enumerate()
464            .map(|(new_idx, old_idx)| (old_idx, new_idx))
465            .collect();
466
467        let vertices_old_to_new_map: HashMap<_, _> = keep_vertices
468            .iter()
469            .enumerate()
470            .map(|(new_idx, old_idx)| (*old_idx, new_idx))
471            .collect();
472
473        let new_vertices = keep_vertices
474            .iter()
475            .map(|old_vertex_idx| self.vertices()[*old_vertex_idx].clone())
476            .collect();
477
478        let mut new_faces = NestedVec::new();
479        for old_face_idx in &keep_faces {
480            let old_face_vertices = self.get_face_connectivity(*old_face_idx).unwrap();
481            let mut new_face_vertices = new_faces.begin_array();
482
483            for old_vertex_idx in old_face_vertices {
484                let new_vertex_idx = vertices_old_to_new_map.get(old_vertex_idx).unwrap();
485                new_face_vertices.push_single(*new_vertex_idx);
486            }
487        }
488
489        let mut new_cells = NestedVec::new();
490        for old_cell_idx in cell_indices {
491            let old_cell_faces = self.get_cell_connectivity(*old_cell_idx).unwrap();
492            let mut new_cell_faces = new_cells.begin_array();
493
494            for old_face_idx in old_cell_faces {
495                let new_face_idx = faces_old_to_new_map.get(old_face_idx).unwrap();
496                new_cell_faces.push_single(*new_face_idx);
497            }
498        }
499
500        Self::from_poly_data(new_vertices, new_faces, new_cells)
501    }
502}
503
504/// Marks vertices according to whether or not they are contained in the half space.
505///
506/// More precisely, given N vertices, a vector of N boolean values is returned.
507/// If element `i` is `true`, then vertex `i` is contained in the half space.
508fn mark_vertices<T: Real>(vertices: &[Point3<T>], half_space: &HalfSpace<T>) -> Vec<bool> {
509    vertices
510        .iter()
511        .map(|v| half_space.contains_point(v))
512        .collect()
513}
514
515#[derive(Copy, Clone, Debug, PartialEq, Eq)]
516pub enum Classification {
517    Inside,
518    Outside,
519    Cut,
520}
521
522//fn classify_face(face_vertices: &[usize], vertex_table: &[bool]) -> Classification {
523//    let num_outside_vertices = face_vertices.iter().map(|i| vertex_table[*i]).count();
524//
525//    if num_outside_vertices == 0 {
526//        Classification::Inside
527//    } else if num_outside_vertices == face_vertices.len() {
528//        Classification::Outside
529//    } else {
530//        Classification::Cut
531//    }
532//}
533
534fn is_intersection_vertex(vertex_edge_representation: &UndirectedEdge) -> bool {
535    let [a, b] = vertex_edge_representation.indices();
536    a != b
537}
538
539impl<T> PolyMesh3d<T>
540where
541    T: Real,
542{
543    pub fn translate(&mut self, translation: &Vector3<T>) {
544        for v in self.vertices_mut() {
545            *v += translation;
546        }
547    }
548
549    pub fn translated(mut self, translation: &Vector3<T>) -> Self {
550        self.translate(translation);
551        self
552    }
553
554    #[replace_float_literals(T::from_f64(literal).unwrap())]
555    pub fn compute_volume(&self) -> T {
556        let boundary_faces = self.find_boundary_faces();
557        let face_iter = boundary_faces
558            .iter()
559            .map(|face_idx| self.get_face(*face_idx).unwrap());
560
561        compute_polyhedron_volume_from_faces(face_iter)
562    }
563
564    pub fn intersect_convex_polyhedron<'a>(&self, polyhedron: &impl ConvexPolyhedron<'a, T>) -> Self {
565        let mut mesh = self.clone();
566        for i in 0..polyhedron.num_faces() {
567            let face = polyhedron.get_face(i).unwrap();
568            if let Some(plane) = face.compute_plane() {
569                let half_space = HalfSpace::from_plane(&plane.flipped());
570                mesh = mesh.intersect_half_space(&half_space);
571            }
572        }
573        mesh
574    }
575
576    pub fn intersect_half_space(&self, half_space: &HalfSpace<T>) -> Self {
577        // Our approach will be to first build up the complete description of faces
578        // by representing vertices as undirected edges between two vertices a and b.
579        // The physical vertex is defined to be the closest point to the half space on the
580        // line segment connecting a and b.
581        // In particular, if a == b, then the vertex is a vertex in the original mesh.
582        // If a != b, then the vertex is an intersection point between an edge and the half space.
583
584        // A vertex is a member of the half space if the half space contains the vertex.
585        let vertex_half_space_membership = mark_vertices(&self.vertices, half_space);
586
587        let mut new_faces = NestedVec::new();
588        let mut face_classifications = Vec::with_capacity(self.num_faces());
589
590        for face_vertices in self.faces.iter() {
591            let mut new_face_vertices = new_faces.begin_array();
592
593            let mut classification = Classification::Inside;
594
595            let face_vertices = face_vertices.iter().chain(face_vertices.first()).copied();
596            for (a, b) in face_vertices.tuple_windows() {
597                let a_is_inside = vertex_half_space_membership[a];
598                let b_is_inside = vertex_half_space_membership[b];
599
600                if a_is_inside {
601                    new_face_vertices.push_single(UndirectedEdge::new(a, a));
602                }
603
604                if a_is_inside != b_is_inside {
605                    // Edge is cut
606                    new_face_vertices.push_single(UndirectedEdge::new(a, b));
607                    classification = Classification::Cut;
608                }
609            }
610
611            // Only vertices which are somehow inside of the half space get added.
612            // Thus, if we added no vertices, the face is entirely outside.
613            if new_face_vertices.count() == 0 {
614                classification = Classification::Outside;
615            }
616
617            face_classifications.push(classification);
618        }
619
620        #[derive(Debug)]
621        struct IntersectionEdge {
622            //            face_idx: usize,
623            a: UndirectedEdge,
624            b: UndirectedEdge,
625        }
626
627        let mut intersection_edges = Vec::new();
628
629        let mut new_cells = NestedVec::new();
630        for cell_faces in self.cells.iter() {
631            intersection_edges.clear();
632            let mut new_cell_faces = new_cells.begin_array();
633
634            for face_idx in cell_faces {
635                let face_classification = face_classifications[*face_idx];
636                match face_classification {
637                    Classification::Inside => {
638                        new_cell_faces.push_single(*face_idx);
639                    }
640                    Classification::Outside => {}
641                    Classification::Cut => {
642                        new_cell_faces.push_single(*face_idx);
643                        let face_vertices = new_faces
644                            .get(*face_idx)
645                            .expect("Invalid face index referenced in cell.");
646                        let face_vertices = face_vertices.iter().chain(face_vertices.first());
647                        for (a, b) in face_vertices.tuple_windows() {
648                            // We're looking for edges that connect two intersection vertices
649                            // (i.e. new vertices that result from the intersection with the plane).
650                            if is_intersection_vertex(a) && is_intersection_vertex(b) {
651                                intersection_edges.push(IntersectionEdge {
652                                    //                                    face_idx: *face_idx,
653                                    a: *a,
654                                    b: *b,
655                                });
656                            }
657                        }
658                    }
659                }
660            }
661
662            // At this point we know which edges that are involved in creating new faces.
663            // In order to connect them together, we pick a random one and then start
664            // stringing them together as long as this is possible. For each such
665            // separate sequence, we generate a new face. Under non-degenerate situations,
666            // only one such face should get created.
667            while let Some(start_edge) = intersection_edges.pop() {
668                // TODO: Are we guaranteed that we don't accidentally pick another intersection
669                // edge from a face we have already visited? I *think* that this is the case,
670                // but I am not sure. We *might* have to additionally keep track of
671                // faces that we have picked?
672
673                let new_face_index = new_faces.len();
674                let mut new_face_vertices = new_faces.begin_array();
675
676                new_face_vertices.push_single(start_edge.a);
677                let mut next_vertex = start_edge.b;
678
679                while let Some(next_local_idx) = intersection_edges
680                    .iter()
681                    .position(|edge| edge.a == next_vertex || edge.b == next_vertex)
682                {
683                    let next_edge = intersection_edges.swap_remove(next_local_idx);
684                    if next_edge.a == next_vertex {
685                        new_face_vertices.push_single(next_edge.a);
686                        next_vertex = next_edge.b;
687                    } else {
688                        new_face_vertices.push_single(next_edge.b);
689                        next_vertex = next_edge.a;
690                    }
691                }
692
693                new_cell_faces.push_single(new_face_index);
694            }
695        }
696
697        // At this point, we have fully constructed all faces and cells,
698        // but vertices are all represented in the point-on-edge representation.
699        // Moreover, we need to remove unused vertices, empty faces (faces that were
700        // removed but kept as empty sets in order to retain stable indexing) and cells.
701        let vertex_label_map = generate_edge_repr_vertex_labels(
702            new_faces
703                .iter()
704                .flat_map(|face_vertices| face_vertices)
705                .copied(),
706        );
707
708        // Compute new vertex coordinates where necessary
709        let mut final_vertices = vec![Point3::origin(); vertex_label_map.len()];
710        for (edge_rep, new_vertex_idx) in &vertex_label_map {
711            let [a, b] = *edge_rep.indices();
712            let vertex_coords = if a == b {
713                self.vertices[a]
714            } else {
715                let v_a = self.vertices[a];
716                let v_b = self.vertices[b];
717                let segment = LineSegment3d::from_end_points(v_a, v_b);
718                segment.closest_point_to_plane(&half_space.plane())
719            };
720            final_vertices[*new_vertex_idx] = vertex_coords;
721        }
722
723        // Convert faces from edge representation to new indices,
724        // and remove and remap empty faces
725        let (final_faces, face_label_map) = relabel_face_edge_representations(&new_faces, &vertex_label_map);
726
727        // TODO: If we're a little more clever earlier on, we wouldn't have to
728        // allocate a whole new storage here
729        let mut final_cells = NestedVec::new();
730        for cell_faces in new_cells.iter() {
731            if !cell_faces.is_empty() {
732                let mut new_cell_faces = final_cells.begin_array();
733                for cell_face in cell_faces {
734                    new_cell_faces.push_single(
735                        *face_label_map
736                            .get(cell_face)
737                            .expect("Logic error: Face index is not in map"),
738                    );
739                }
740            }
741        }
742
743        PolyMesh3d::from_poly_data(final_vertices, final_faces, final_cells)
744    }
745}
746
747fn generate_edge_repr_vertex_labels(
748    vertices_in_edge_repr: impl IntoIterator<Item = UndirectedEdge>,
749) -> BTreeMap<UndirectedEdge, usize> {
750    let mut map = BTreeMap::new();
751    let mut iter = vertices_in_edge_repr.into_iter();
752
753    let mut next_available_index = 0;
754    while let Some(vertex) = iter.next() {
755        map.entry(vertex).or_insert_with(|| {
756            let idx = next_available_index;
757            next_available_index += 1;
758            idx
759        });
760    }
761
762    map
763}
764
765/// Computes the standard index representation given edge representations of a collection
766/// of faces and a label mapping for vertices.
767///
768/// Empty faces are removed as part of the process. A mapping is returned which
769/// serves to map "old" face indices to new indices.
770fn relabel_face_edge_representations(
771    faces: &NestedVec<UndirectedEdge>,
772    vertex_label_map: &BTreeMap<UndirectedEdge, usize>,
773) -> (NestedVec<usize>, HashMap<usize, usize>) {
774    let mut new_faces = NestedVec::new();
775    let mut face_label_map = HashMap::new();
776    let mut next_available_index = 0;
777    {
778        for (i, face_vertices) in faces.iter().enumerate() {
779            if !face_vertices.is_empty() {
780                let mut new_face_vertices = new_faces.begin_array();
781                for vertex_edge_rep in face_vertices {
782                    new_face_vertices.push_single(
783                        *vertex_label_map
784                            .get(vertex_edge_rep)
785                            .expect("Logic error: Label map must map all relevant vertices."),
786                    );
787                }
788
789                face_label_map.insert(i, next_available_index);
790                next_available_index += 1;
791            }
792        }
793    }
794
795    (new_faces, face_label_map)
796}
797
798impl<T> Display for PolyMesh3d<T>
799where
800    T: Scalar + Display,
801{
802    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
803        writeln!(f, "PolyMesh3d")?;
804        writeln!(f, "  Vertices:")?;
805        for v in self.vertices() {
806            writeln!(f, "    {}", v)?;
807        }
808        writeln!(f)?;
809
810        writeln!(f, "  Face vertex indices:")?;
811        for (i, vertex_indices) in self.face_connectivity_iter().enumerate() {
812            write!(f, "    {}: ", i)?;
813            if let Some(first) = vertex_indices.first() {
814                write!(f, "{}", first)?;
815            }
816            for idx in vertex_indices.iter().skip(1) {
817                write!(f, ", {}", idx)?;
818            }
819            writeln!(f)?;
820        }
821        writeln!(f)?;
822
823        writeln!(f, "  Cell face indices:")?;
824        for (i, face_indices) in self.cell_connectivity_iter().enumerate() {
825            write!(f, "    {}: ", i)?;
826            if let Some(first) = face_indices.first() {
827                write!(f, "{}", first)?;
828            }
829            for idx in face_indices.iter().skip(1) {
830                write!(f, ", {}", idx)?;
831            }
832            writeln!(f)?;
833        }
834
835        Ok(())
836    }
837}