Skip to main content

geop_core_topology/
face.rs

1use super::ids::ShellId;
2use crate::boundary::{BoundaryIndex, BoundaryType};
3use geop_core_geometry::nurb_surface::NurbSurface3D;
4use geop_core_math::scalars::Scalar;
5
6/// A trimmed patch of `surface`: everything inside `outer` and outside every
7/// loop in `holes`.
8///
9/// The two are deliberately separate fields rather than one list with the
10/// outer loop by convention at index 0. A face always has exactly one outer
11/// boundary — that is what makes it a face — while holes are a genuinely
12/// variable collection, and encoding that in the type means no code has to
13/// re-establish it. It also removes a whole class of bug where a hole is
14/// accidentally treated as the outer loop (or vice versa) after a
15/// split/merge reorders the list.
16#[derive(Clone, Debug)]
17pub struct Face<S: Scalar> {
18    pub surface: NurbSurface3D<S>,
19    pub outer: BoundaryType,
20    pub holes: Vec<BoundaryType>,
21    pub shell: ShellId,
22}
23
24impl<S: Scalar> Face<S> {
25    /// Every boundary, outer loop first.
26    pub fn boundaries(&self) -> impl Iterator<Item = BoundaryType> + '_ {
27        std::iter::once(self.outer).chain(self.holes.iter().copied())
28    }
29
30    /// Every boundary, outer loop first, for in-place rewriting — used by
31    /// the operations that re-anchor or rename boundaries wholesale (a
32    /// vertex being merged away, a loop's anchor coedge being deleted)
33    /// without caring which kind each one is.
34    pub fn boundaries_mut(&mut self) -> impl Iterator<Item = &mut BoundaryType> + '_ {
35        std::iter::once(&mut self.outer).chain(self.holes.iter_mut())
36    }
37
38    /// The boundary `index` names, or `None` if it names a hole this face
39    /// does not have.
40    pub fn boundary(&self, index: BoundaryIndex) -> Option<BoundaryType> {
41        match index {
42            BoundaryIndex::Outer => Some(self.outer),
43            BoundaryIndex::Hole(i) => self.holes.get(i).copied(),
44        }
45    }
46
47    /// Re-anchor the boundary `index` names on a different loop. Needed after
48    /// any restructuring that rewires `next`/`prev`, since the previous
49    /// anchor may no longer sit on the ring the boundary now describes.
50    pub fn set_boundary(&mut self, index: BoundaryIndex, boundary: BoundaryType) -> bool {
51        match index {
52            BoundaryIndex::Outer => {
53                self.outer = boundary;
54                true
55            }
56            BoundaryIndex::Hole(i) => match self.holes.get_mut(i) {
57                Some(slot) => {
58                    *slot = boundary;
59                    true
60                }
61                None => false,
62            },
63        }
64    }
65}