Skip to main content

draco_oxide_core/corner_table/
mod.rs

1pub mod all_inclusive_corner_table;
2pub mod attribute_corner_table;
3
4use std::collections::BTreeMap;
5
6use crate::attribute::Attribute;
7use crate::types::{
8    AttributeValueIdx, CornerIdx, FaceIdx, PointIdx, VecCornerIdx, VecVertexIdx, VertexIdx,
9};
10
11pub trait GenericCornerTable {
12    fn face_idx_containing(&self, corner: CornerIdx) -> FaceIdx;
13    fn num_faces(&self) -> usize;
14    fn num_corners(&self) -> usize;
15    fn num_vertices(&self) -> usize;
16    fn point_idx(&self, corner: CornerIdx) -> PointIdx;
17    fn vertex_idx(&self, corner: CornerIdx) -> VertexIdx;
18    fn opposite(&self, corner: CornerIdx) -> Option<CornerIdx>;
19    fn previous(&self, corner: CornerIdx) -> CornerIdx;
20    fn next(&self, corner: CornerIdx) -> CornerIdx;
21    fn left_most_corner(&self, vertex: VertexIdx) -> CornerIdx;
22
23    fn swing_right(&self, corner: CornerIdx) -> Option<CornerIdx> {
24        if let Some(c) = self.opposite(self.previous(corner)) {
25            Some(self.previous(c))
26        } else {
27            None
28        }
29    }
30
31    fn swing_left(&self, corner: CornerIdx) -> Option<CornerIdx> {
32        if let Some(c) = self.opposite(self.next(corner)) {
33            Some(self.next(c))
34        } else {
35            None
36        }
37    }
38
39    fn is_on_boundary(&self, v: VertexIdx) -> bool {
40        self.swing_left(self.left_most_corner(v)).is_none()
41    }
42
43    fn get_left_corner(&self, corner: CornerIdx) -> Option<CornerIdx> {
44        self.opposite(self.previous(corner))
45    }
46
47    fn get_right_corner(&self, corner: CornerIdx) -> Option<CornerIdx> {
48        self.opposite(self.next(corner))
49    }
50
51    #[allow(unused)]
52    fn vertex_to_attribute_map(&self) -> Option<&VecVertexIdx<AttributeValueIdx>> {
53        None
54    }
55}
56
57#[derive(Debug, Clone)]
58pub struct CornerTable<'mesh> {
59    /// Records the opposite corner for each corner.
60    /// If a corner does not have an opposite corner, the value is 'CornerIdx::from(usize::MAX)'.
61    opposite_corners: VecCornerIdx<CornerIdx>,
62
63    /// faces of the mesh.
64    mesh_faces: &'mesh [[PointIdx; 3]],
65
66    /// Faces of the POSITION ATTRIBUTE.
67    /// This is different from the faces of the mesh.
68    conn_faces: Vec<[VertexIdx; 3]>,
69
70    /// Number of corners in the mesh.
71    num_corners: usize,
72
73    // Number of vertices in the mesh.
74    num_vertices: usize,
75
76    /// Stores the left most corner for each vertex.
77    left_most_corners: VecVertexIdx<CornerIdx>,
78
79    /// corner to vertex map.
80    corner_to_vertex: BTreeMap<CornerIdx, VertexIdx>,
81
82    /// Stores the parents of non-manifold vertices.
83    non_manifold_vertex_parents: Vec<VertexIdx>,
84}
85
86impl<'mesh> CornerTable<'mesh> {
87    pub fn new(mesh_faces: &'mesh [[PointIdx; 3]], pos_att: &Attribute) -> Self {
88        let conn_faces = mesh_faces
89            .iter()
90            .map(|f| {
91                [
92                    usize::from(pos_att.get_unique_val_idx(f[0])).into(),
93                    usize::from(pos_att.get_unique_val_idx(f[1])).into(),
94                    usize::from(pos_att.get_unique_val_idx(f[2])).into(),
95                ]
96            })
97            .collect::<Vec<_>>();
98        let mut out = Self {
99            opposite_corners: VecCornerIdx::new(), // will be computed later
100            num_corners: mesh_faces.len() * 3,
101            mesh_faces,
102            conn_faces,
103            num_vertices: 0,                         // will be computed later
104            left_most_corners: VecVertexIdx::new(),  // will be computed later
105            corner_to_vertex: BTreeMap::new(),       // will be computed later
106            non_manifold_vertex_parents: Vec::new(), // will be computed later
107        };
108
109        let unused_vertices = Self::get_unused_vertices(&out.conn_faces);
110        if !unused_vertices.is_empty() {
111            panic!(
112                "Mesh contains unused vertices: {:?}. This is not supported by the corner table.",
113                unused_vertices
114            );
115        }
116
117        out.compute_table();
118        if Self::contains_non_manifold_edges(&out.conn_faces) {
119            out.handle_no_manifold_edges();
120        }
121        out.compute_left_most_corners();
122
123        out
124    }
125
126    /// checks if the mesh has non-manifold edges.
127    fn contains_non_manifold_edges(faces: &[[VertexIdx; 3]]) -> bool {
128        let mut edges = faces
129            .iter()
130            .flat_map(|f| {
131                let v0 = f[0];
132                let v1 = f[1];
133                let v2 = f[2];
134                vec![[v0, v1], [v1, v2], [v2, v0]]
135            })
136            .collect::<Vec<_>>();
137        for e in &mut edges {
138            e.sort();
139        }
140        edges.sort();
141        // count duplicates. If there is a triple of the same edge, it is non-manifold.
142        let mut count = 1;
143        for i in 1..edges.len() {
144            if edges[i] == edges[i - 1] {
145                count += 1;
146                if count > 2 {
147                    return true; // found a non-manifold edge
148                }
149            } else {
150                count = 1; // reset count
151            }
152        }
153        false // no non-manifold edges found
154    }
155
156    /// Handles non-manifold edges by breaking the connectivity at them.
157    /// Follows the draco's implementation.
158    fn handle_no_manifold_edges(&mut self) {
159        let mut visited_corners = vec![false; self.num_corners()];
160        let mut sink_vertices: Vec<(VertexIdx, CornerIdx)> = Vec::new();
161        let mut connectivity_updated;
162        let default_opposite = CornerIdx::from(usize::MAX);
163        loop {
164            connectivity_updated = false;
165            for c in 0..self.num_corners() {
166                if visited_corners[c] {
167                    continue;
168                }
169                let c = CornerIdx::from(c);
170                sink_vertices.clear();
171
172                // Swing all the way to find the lefft most corner, if any.
173                let mut first_c = c;
174                let mut curr_c = c;
175                while let Some(next_c) = self.swing_left(curr_c) {
176                    if next_c == first_c || visited_corners[usize::from(next_c)] {
177                        break;
178                    }
179                    curr_c = next_c;
180                }
181
182                first_c = curr_c;
183
184                // Check for the uniqueness by swinging right.
185                loop {
186                    visited_corners[usize::from(curr_c)] = true;
187                    let sink_c = self.next(curr_c);
188                    let sink_v = self.corner_to_vert(sink_c);
189
190                    let edge_c = self.previous(curr_c);
191                    let mut vertex_connectivity_updated = false;
192
193                    for &attached_sink_vertex in &sink_vertices {
194                        if attached_sink_vertex.0 == sink_v {
195                            let other_edge_c = attached_sink_vertex.1;
196                            let opp_edge_c = self.opposite(edge_c);
197
198                            if let Some(opp_edge_c) = opp_edge_c {
199                                if opp_edge_c == other_edge_c {
200                                    continue;
201                                }
202                            }
203
204                            let opp_other_edge_c = self.opposite(other_edge_c);
205                            if let Some(opp_edge_c) = opp_edge_c {
206                                self.opposite_corners[opp_edge_c] = default_opposite;
207                                // None
208                            }
209                            if let Some(opp_other_edge_c) = opp_other_edge_c {
210                                self.opposite_corners[opp_other_edge_c] = default_opposite;
211                                // None
212                            }
213
214                            self.opposite_corners[edge_c] = default_opposite;
215                            self.opposite_corners[other_edge_c] = default_opposite;
216
217                            vertex_connectivity_updated = true;
218                            break;
219                        }
220                    }
221                    if vertex_connectivity_updated {
222                        connectivity_updated = true;
223                        break;
224                    }
225                    let new_sink_vert: (VertexIdx, CornerIdx) =
226                        (self.corner_to_vert(self.previous(curr_c)), sink_c);
227                    sink_vertices.push(new_sink_vert);
228
229                    curr_c = if let Some(c) = self.swing_right(curr_c) {
230                        c
231                    } else {
232                        break; // reached the end of the corner loop
233                    };
234                    if curr_c == first_c {
235                        break; // reached back to the first corner
236                    }
237                }
238            }
239            if !connectivity_updated {
240                break; // no more connectivity updates
241            }
242        }
243    }
244
245    fn get_unused_vertices(faces: &[[VertexIdx; 3]]) -> Vec<usize> {
246        let num_vertices = {
247            let num_vertices_minus_1 = *faces.iter().flatten().max().unwrap_or(&VertexIdx::from(0));
248            usize::from(num_vertices_minus_1) + 1
249        };
250        let mut used_vertices = vec![false; num_vertices];
251        for f in faces {
252            for &v in f {
253                used_vertices[usize::from(v)] = true;
254            }
255        }
256        used_vertices
257            .iter()
258            .enumerate()
259            .filter_map(|(idx, &used)| if !used { Some(idx) } else { None })
260            .collect()
261    }
262
263    fn compute_table(&mut self) {
264        let default_opposite = CornerIdx::from(usize::MAX);
265        let default_vertex = VertexIdx::from(usize::MAX);
266        self.opposite_corners
267            .resize(self.num_corners(), default_opposite);
268        let mut num_corners_on_vertices = VecVertexIdx::with_capacity(self.num_corners());
269
270        // Compute the number of corners on each vertex.
271        // The vertices are sorted by their earliest corner.
272        for c in 0..self.num_corners() {
273            let c = CornerIdx::from(c);
274            let v1 = self.vertex_idx(c);
275            if usize::from(v1) >= num_corners_on_vertices.len() {
276                num_corners_on_vertices.resize(usize::from(v1) + 1, 0);
277            }
278            num_corners_on_vertices[v1] += 1;
279        }
280
281        // Array for storing half edges. (sink vertex, edge corner)
282        let mut vertex_edges: Vec<(VertexIdx, CornerIdx)> =
283            vec![(default_vertex, default_opposite); self.num_corners()];
284
285        let mut offset = 0;
286        // Compute the offset of the the earliest corner for each vertex.
287        let vertex_offset: VecVertexIdx<_> = (0..num_corners_on_vertices.len())
288            .map(|i| {
289                let i = VertexIdx::from(i);
290                let out = offset;
291                offset += num_corners_on_vertices[i];
292                out
293            })
294            .collect::<Vec<_>>()
295            .into();
296
297        for c in 0..self.num_corners() {
298            let c = CornerIdx::from(c);
299            let tip_v = self.vertex_idx(c);
300            let source_v = self.vertex_idx(self.next(c));
301            let sink_v = self.vertex_idx(self.previous(c));
302
303            let f_idx = self.face_idx_containing(c);
304            if c == Self::first_corner(f_idx) {
305                let v0 = self.vertex_idx(c);
306                if v0 == source_v || v0 == sink_v || source_v == sink_v {
307                    continue; // skip degenerate corners
308                }
309            }
310
311            let mut opposite_c = default_opposite;
312            let num_corners_on_vert = num_corners_on_vertices[sink_v];
313            offset = vertex_offset[sink_v];
314            for i in 0..num_corners_on_vert {
315                let other_v = vertex_edges[offset].0;
316                if other_v == default_vertex {
317                    break;
318                }
319                if other_v == source_v {
320                    // opposite corner found.
321                    // We need to remove the half edge from the vertex_edges.
322                    if tip_v == self.vertex_idx(vertex_edges[offset].1) {
323                        continue;
324                    }
325                    opposite_c = vertex_edges[offset].1;
326                    for _ in i + 1..num_corners_on_vert {
327                        vertex_edges[offset] = vertex_edges[offset + 1];
328                        if vertex_edges[offset].0 == default_vertex {
329                            break;
330                        }
331                        offset += 1;
332                    }
333                    vertex_edges[offset].0 = default_vertex;
334                    break;
335                }
336                offset += 1;
337            }
338            if opposite_c == default_opposite {
339                let num_corners_on_source_vert = num_corners_on_vertices[source_v];
340                let first_c = vertex_offset[source_v];
341                for corner in first_c..num_corners_on_source_vert + first_c {
342                    if vertex_edges[corner].0 == default_vertex {
343                        vertex_edges[corner].0 = sink_v;
344                        vertex_edges[corner].1 = c;
345                        break;
346                    }
347                }
348            } else {
349                self.opposite_corners[c] = opposite_c;
350                self.opposite_corners[opposite_c] = c;
351            }
352        }
353        self.num_vertices = num_corners_on_vertices.len();
354    }
355
356    fn compute_left_most_corners(&mut self) {
357        let default_corner = CornerIdx::from(usize::MAX);
358        self.left_most_corners
359            .resize(self.num_vertices(), default_corner);
360        let mut visited_vertices = VecVertexIdx::from(vec![false; self.num_vertices()]);
361        let mut visited_corners = VecCornerIdx::from(vec![false; self.num_corners()]);
362
363        {
364            let mut vertices = VecVertexIdx::from(vec![false; self.num_vertices()]);
365            for f_idx in 0..self.get_mesh_faces().len() {
366                for i in 0..3 {
367                    let c = CornerIdx::from(3 * f_idx + i);
368                    let v = self.vertex_idx(c);
369                    if !vertices[v] {
370                        vertices[v] = true;
371                    }
372                }
373            }
374        }
375
376        for f_idx in 0..self.get_mesh_faces().len() {
377            for i in 0..3 {
378                let c = CornerIdx::from(3 * f_idx + i);
379                if visited_corners[c] {
380                    continue;
381                }
382
383                let mut v = self.vertex_idx(c);
384                let mut is_non_manifold_vertex = false;
385                if visited_vertices[v] {
386                    // Coming here means the vertex has a neighborhood that is not connected when the vertex is removed,
387                    // i.e. it is violating the manifold condition.
388                    // We need to create a new vertex here to avoid this case.
389                    self.left_most_corners.push(default_corner);
390                    self.non_manifold_vertex_parents.push(v);
391                    visited_vertices.push(false);
392                    v = VertexIdx::from(self.num_vertices);
393                    self.num_vertices += 1;
394                    is_non_manifold_vertex = true;
395                }
396                visited_vertices[v] = true;
397                visited_corners[c] = true;
398                self.left_most_corners[v] = c;
399                if is_non_manifold_vertex {
400                    // Update vertex index in the corresponding face.
401                    self.corner_to_vertex.insert(c, v);
402                }
403
404                // Swing all the way to the left
405                let mut maybe_act_c = self.swing_left(c);
406                while let Some(act_c) = maybe_act_c {
407                    if act_c == c {
408                        // Reached back to the initial corner.
409                        break;
410                    }
411                    visited_corners[act_c] = true;
412                    self.left_most_corners[v] = act_c;
413                    if is_non_manifold_vertex {
414                        // Update vertex index in the corresponding face.
415                        self.corner_to_vertex.insert(act_c, v);
416                    }
417                    maybe_act_c = self.swing_left(act_c);
418                }
419
420                if maybe_act_c.is_none() {
421                    // if we have reached open boundary, we need to swing right to mark all corners
422                    maybe_act_c = Some(c);
423                    while let Some(act_c) = maybe_act_c {
424                        visited_corners[act_c] = true;
425                        if is_non_manifold_vertex {
426                            self.corner_to_vertex.insert(act_c, v);
427                        }
428                        maybe_act_c = self.swing_right(act_c);
429                    }
430                }
431            }
432        }
433    }
434
435    #[inline]
436    pub fn vertex_valence(&self, v: VertexIdx) -> usize {
437        let c = self.left_most_corner(v);
438        let mut count = 2;
439        while let Some(next_c) = self.swing_right(c) {
440            if next_c == c {
441                count -= 1;
442                break; // we have reached back to the initial corner
443            }
444            count += 1;
445        }
446        count
447    }
448
449    #[inline]
450    pub fn first_corner(face_idx: FaceIdx) -> CornerIdx {
451        CornerIdx::from(usize::from(face_idx) * 3)
452    }
453
454    #[inline]
455    pub fn get_mesh_faces(&self) -> &[[PointIdx; 3]] {
456        self.mesh_faces
457    }
458
459    #[inline]
460    pub fn corner_to_vert(&self, corner: CornerIdx) -> VertexIdx {
461        if let Some(v) = self.corner_to_vertex.get(&corner) {
462            return *v;
463        };
464
465        let corner = usize::from(corner);
466
467        let local = corner % 3;
468        let face_idx = corner / 3;
469
470        match local {
471            0 => self.conn_faces[face_idx][0],
472            1 => self.conn_faces[face_idx][1],
473            2 => self.conn_faces[face_idx][2],
474            _ => unreachable!(), // it is safe to assume this as 'local' is the remainder of a number divided by 3.
475        }
476    }
477}
478
479impl<'mesh> GenericCornerTable for CornerTable<'mesh> {
480    #[inline]
481    fn face_idx_containing(&self, corner: CornerIdx) -> FaceIdx {
482        (usize::from(corner) / 3).into()
483    }
484
485    #[inline]
486    fn num_faces(&self) -> usize {
487        self.get_mesh_faces().len()
488    }
489
490    #[inline]
491    fn num_corners(&self) -> usize {
492        self.num_corners
493    }
494
495    #[inline]
496    fn num_vertices(&self) -> usize {
497        self.num_vertices
498    }
499
500    #[inline]
501    fn point_idx(&self, corner: CornerIdx) -> PointIdx {
502        let corner = usize::from(corner);
503        self.get_mesh_faces()[corner / 3][corner % 3]
504    }
505
506    #[inline]
507    fn vertex_idx(&self, corner: CornerIdx) -> VertexIdx {
508        self.corner_to_vert(corner)
509    }
510
511    #[inline]
512    fn opposite(&self, corner: CornerIdx) -> Option<CornerIdx> {
513        if self.opposite_corners[corner] == CornerIdx::from(usize::MAX) {
514            None
515        } else {
516            Some(self.opposite_corners[corner])
517        }
518    }
519
520    #[inline]
521    fn previous(&self, corner: CornerIdx) -> CornerIdx {
522        let corner = usize::from(corner);
523        let out = if corner % 3 == 0 {
524            corner + 2
525        } else {
526            corner - 1
527        };
528        CornerIdx::from(out)
529    }
530
531    #[inline]
532    fn next(&self, corner: CornerIdx) -> CornerIdx {
533        let corner = usize::from(corner);
534        let out = if corner % 3 == 2 {
535            corner - 2
536        } else {
537            corner + 1
538        };
539        CornerIdx::from(out)
540    }
541
542    #[inline]
543    fn left_most_corner(&self, vertex: VertexIdx) -> CornerIdx {
544        self.left_most_corners[vertex]
545    }
546}
547
548#[cfg(test)]
549mod tests {
550    use crate::attribute::{AttributeDomain, AttributeType};
551    use crate::types::NdVector;
552
553    use super::*;
554
555    #[test]
556    fn test_corner_table() {
557        let faces = vec![
558            [PointIdx::from(0), PointIdx::from(1), PointIdx::from(2)],
559            [PointIdx::from(2), PointIdx::from(1), PointIdx::from(3)],
560        ];
561        let att = Attribute::new(
562            vec![
563                NdVector::from([0_f32, 0.0]),
564                NdVector::from([1_f32, 0.0]),
565                NdVector::from([0_f32, 1.0]),
566                NdVector::from([1_f32, 1.0]),
567            ],
568            AttributeType::Position,
569            AttributeDomain::Position,
570            vec![],
571        );
572
573        let corner_table = CornerTable::new(&faces, &att);
574        assert_eq!(corner_table.num_faces(), 2);
575        assert_eq!(corner_table.num_corners(), 6);
576        assert_eq!(corner_table.num_vertices(), 4);
577        assert_eq!(
578            corner_table.face_idx_containing(CornerIdx::from(0)),
579            FaceIdx::from(0)
580        );
581        assert_eq!(
582            corner_table.face_idx_containing(CornerIdx::from(1)),
583            FaceIdx::from(0)
584        );
585        assert_eq!(
586            corner_table.face_idx_containing(CornerIdx::from(2)),
587            FaceIdx::from(0)
588        );
589        assert_eq!(
590            corner_table.face_idx_containing(CornerIdx::from(3)),
591            FaceIdx::from(1)
592        );
593        assert_eq!(
594            corner_table.face_idx_containing(CornerIdx::from(4)),
595            FaceIdx::from(1)
596        );
597        assert_eq!(
598            corner_table.face_idx_containing(CornerIdx::from(5)),
599            FaceIdx::from(1)
600        );
601        assert!(corner_table.corner_to_vertex.is_empty());
602        assert_eq!(
603            corner_table.opposite(CornerIdx::from(0)),
604            Some(CornerIdx::from(5))
605        );
606        assert_eq!(corner_table.opposite(CornerIdx::from(1)), None);
607        assert_eq!(corner_table.opposite(CornerIdx::from(2)), None);
608        assert_eq!(corner_table.opposite(CornerIdx::from(3)), None);
609        assert_eq!(corner_table.opposite(CornerIdx::from(4)), None);
610        assert_eq!(
611            corner_table.opposite(CornerIdx::from(5)),
612            Some(CornerIdx::from(0))
613        );
614        assert_eq!(
615            corner_table.previous(CornerIdx::from(0)),
616            CornerIdx::from(2)
617        );
618        assert_eq!(
619            corner_table.previous(CornerIdx::from(1)),
620            CornerIdx::from(0)
621        );
622        assert_eq!(
623            corner_table.previous(CornerIdx::from(2)),
624            CornerIdx::from(1)
625        );
626        assert_eq!(corner_table.next(CornerIdx::from(0)), CornerIdx::from(1));
627        assert_eq!(corner_table.next(CornerIdx::from(1)), CornerIdx::from(2));
628        assert_eq!(corner_table.next(CornerIdx::from(2)), CornerIdx::from(0));
629    }
630
631    #[test]
632    fn test_no_att_seam() {
633        let faces = vec![
634            [PointIdx::from(0), PointIdx::from(1), PointIdx::from(2)],
635            [PointIdx::from(1), PointIdx::from(3), PointIdx::from(2)],
636            [PointIdx::from(2), PointIdx::from(3), PointIdx::from(4)],
637            [PointIdx::from(2), PointIdx::from(4), PointIdx::from(5)],
638        ];
639        let att = Attribute::new(
640            // Some non-duplicated positions
641            vec![
642                NdVector::from([0_f32, 0.0, 0.0]),
643                NdVector::from([1_f32, 0.0, 0.0]),
644                NdVector::from([0_f32, 1.0, 0.0]),
645                NdVector::from([1_f32, 1.0, 0.0]),
646                NdVector::from([0_f32, 0.5, 0.0]),
647                NdVector::from([1_f32, 0.5, 0.0]),
648            ],
649            AttributeType::Position,
650            AttributeDomain::Position,
651            vec![],
652        );
653
654        let corner_table = CornerTable::new(&faces, &att);
655        assert_eq!(corner_table.num_faces(), 4);
656        assert_eq!(corner_table.num_corners(), 12);
657        assert_eq!(corner_table.num_vertices(), 6);
658        assert!(corner_table.corner_to_vertex.is_empty());
659    }
660
661    #[test]
662    fn test_triangle() {
663        let faces = vec![[PointIdx::from(0), PointIdx::from(1), PointIdx::from(2)]];
664        let att = Attribute::new(
665            vec![
666                NdVector::from([0_f32, 0.0]),
667                NdVector::from([1_f32, 0.0]),
668                NdVector::from([0_f32, 1.0]),
669            ],
670            AttributeType::Position,
671            AttributeDomain::Position,
672            vec![],
673        );
674
675        let corner_table = CornerTable::new(&faces, &att);
676        assert_eq!(corner_table.num_faces(), 1);
677        assert_eq!(corner_table.num_corners(), 3);
678        assert_eq!(corner_table.num_vertices(), 3);
679        assert_eq!(
680            corner_table.left_most_corners,
681            VecVertexIdx::from(vec![
682                CornerIdx::from(0),
683                CornerIdx::from(1),
684                CornerIdx::from(2)
685            ])
686        );
687    }
688
689    #[test]
690    fn test_non_manifold() {
691        let faces = vec![
692            [PointIdx::from(0), PointIdx::from(1), PointIdx::from(2)],
693            [PointIdx::from(0), PointIdx::from(3), PointIdx::from(4)],
694        ];
695        let att = Attribute::new(
696            vec![
697                NdVector::from([0_f32, 0.0]),
698                NdVector::from([1_f32, 0.0]),
699                NdVector::from([0_f32, 1.0]),
700                NdVector::from([-1_f32, 1.0]),
701                NdVector::from([0_f32, -1.0]),
702            ],
703            AttributeType::Position,
704            AttributeDomain::Position,
705            vec![],
706        );
707
708        let corner_table = CornerTable::new(&faces, &att);
709        assert_eq!(corner_table.num_faces(), 2);
710        assert_eq!(corner_table.num_corners(), 6);
711        assert_eq!(corner_table.num_vertices(), 6); // Vertex 0 is non-manifold, so it is duplicated.
712        let answer: VecVertexIdx<CornerIdx> = vec![0, 1, 2, 4, 5, 3]
713            .into_iter()
714            .map(|x| CornerIdx::from(x))
715            .collect::<Vec<_>>()
716            .into();
717        assert_eq!(corner_table.left_most_corners, answer);
718    }
719
720    #[test]
721    fn test_non_manifold_with_seam() {
722        let faces = vec![
723            [VertexIdx::from(0), VertexIdx::from(1), VertexIdx::from(2)],
724            [VertexIdx::from(1), VertexIdx::from(3), VertexIdx::from(2)],
725            [VertexIdx::from(2), VertexIdx::from(1), VertexIdx::from(4)],
726        ];
727        assert!(
728            CornerTable::contains_non_manifold_edges(&faces),
729            "The mesh should contain non-manifold edges, but it does not."
730        );
731    }
732
733    // ToDo: Add tests for non-manifold vertices cases.
734}