fj_kernel/objects/full/
face.rs

1use std::collections::{btree_set, BTreeSet};
2
3use fj_interop::mesh::Color;
4use fj_math::Winding;
5
6use crate::{
7    objects::{Cycle, Surface},
8    storage::Handle,
9};
10
11/// A face of a shape
12///
13/// A `Face` is a bounded area of a [`Surface`], the [`Surface`] itself being an
14/// infinite 2-dimensional object in 3D space. `Face`s are bound by one exterior
15/// cycle, which defines the outer boundary, and an arbitrary number of interior
16/// cycles (i.e. holes).
17///
18/// `Face` has a defined orientation, a front and a back side. When faces are
19/// combined into [`Shell`]s, the face orientation defines what is inside and
20/// outside of the shell. This stands in contrast to [`Surface`], which has no
21/// defined orientation.
22///
23/// You can look at a `Face` from two directions: front and back. The winding of
24/// the exterior cycle will be clockwise or counter-clockwise, depending on your
25/// perspective. The front side of the face, is the side where from which the
26/// exterior cycle appear counter-clockwise.
27///
28/// Interior cycles must have the opposite winding of the exterior cycle,
29/// meaning on the front side of the face, they must appear clockwise. This
30/// means that all [`HalfEdge`]s that bound a `Face` have the interior of the
31/// face on their left side (on the face's front side).
32///
33/// [`HalfEdge`]: crate::objects::HalfEdge
34/// [`Shell`]: crate::objects::Shell
35#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
36pub struct Face {
37    surface: Handle<Surface>,
38    exterior: Handle<Cycle>,
39    interiors: Vec<Handle<Cycle>>,
40    color: Option<Color>,
41}
42
43impl Face {
44    /// Construct an instance of `Face`
45    pub fn new(
46        surface: Handle<Surface>,
47        exterior: Handle<Cycle>,
48        interiors: impl IntoIterator<Item = Handle<Cycle>>,
49        color: Option<Color>,
50    ) -> Self {
51        let interiors = interiors.into_iter().collect();
52
53        Self {
54            surface,
55            exterior,
56            interiors,
57            color,
58        }
59    }
60
61    /// Access the surface of the face
62    pub fn surface(&self) -> &Handle<Surface> {
63        &self.surface
64    }
65
66    /// Access the cycle that bounds the face on the outside
67    pub fn exterior(&self) -> &Handle<Cycle> {
68        &self.exterior
69    }
70
71    /// Access the cycles that bound the face on the inside
72    ///
73    /// Each of these cycles defines a hole in the face.
74    pub fn interiors(&self) -> impl Iterator<Item = &Handle<Cycle>> + '_ {
75        self.interiors.iter()
76    }
77
78    /// Access all cycles of the face (both exterior and interior)
79    pub fn all_cycles(&self) -> impl Iterator<Item = &Handle<Cycle>> + '_ {
80        [self.exterior()].into_iter().chain(self.interiors())
81    }
82
83    /// Access the color of the face
84    pub fn color(&self) -> Option<Color> {
85        self.color
86    }
87
88    /// Determine handed-ness of the face's front-side coordinate system
89    ///
90    /// A face is defined on a surface, which has a coordinate system. Since
91    /// surfaces aren't considered to have an orientation, their coordinate
92    /// system can be considered to be left-handed or right-handed, depending on
93    /// which side of the surface you're looking at.
94    ///
95    /// Faces *do* have an orientation, meaning they have definite front and
96    /// back sides. The front side is the side, where the face's exterior cycle
97    /// is wound counter-clockwise.
98    pub fn coord_handedness(&self) -> Handedness {
99        match self.exterior().winding() {
100            Winding::Ccw => Handedness::RightHanded,
101            Winding::Cw => Handedness::LeftHanded,
102        }
103    }
104}
105
106/// A collection of faces
107#[derive(Clone, Debug, Default, Eq, PartialEq, Hash, Ord, PartialOrd)]
108pub struct FaceSet {
109    inner: BTreeSet<Handle<Face>>,
110}
111
112impl FaceSet {
113    /// Create an empty instance of `Faces`
114    pub fn new() -> Self {
115        Self::default()
116    }
117
118    /// Find the given face
119    pub fn find(&self, face: &Handle<Face>) -> Option<Handle<Face>> {
120        for f in self {
121            if f == face {
122                return Some(f.clone());
123            }
124        }
125
126        None
127    }
128}
129
130impl Extend<Handle<Face>> for FaceSet {
131    fn extend<T: IntoIterator<Item = Handle<Face>>>(&mut self, iter: T) {
132        self.inner.extend(iter);
133    }
134}
135
136impl FromIterator<Handle<Face>> for FaceSet {
137    fn from_iter<T: IntoIterator<Item = Handle<Face>>>(iter: T) -> Self {
138        let mut faces = Self::new();
139        faces.extend(iter);
140        faces
141    }
142}
143
144impl IntoIterator for FaceSet {
145    type Item = Handle<Face>;
146    type IntoIter = btree_set::IntoIter<Handle<Face>>;
147
148    fn into_iter(self) -> Self::IntoIter {
149        self.inner.into_iter()
150    }
151}
152
153impl<'a> IntoIterator for &'a FaceSet {
154    type Item = &'a Handle<Face>;
155    type IntoIter = btree_set::Iter<'a, Handle<Face>>;
156
157    fn into_iter(self) -> Self::IntoIter {
158        self.inner.iter()
159    }
160}
161
162/// The handedness of a face's coordinate system
163///
164/// See [`Face::coord_handedness`].
165#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
166pub enum Handedness {
167    /// The face's coordinate system is left-handed
168    LeftHanded,
169
170    /// The face's coordinate system is right-handed
171    RightHanded,
172}