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}