fj_core/objects/kinds/face.rs
1use fj_math::Winding;
2
3use crate::{
4 geometry::Geometry,
5 objects::{Region, Surface},
6 storage::{Handle, HandleWrapper},
7};
8
9/// A face of a shape
10///
11/// A `Face` is a bounded area of a [`Surface`], the [`Surface`] itself being an
12/// infinite 2-dimensional object in 3D space. `Face`s are bound by one exterior
13/// cycle, which defines the outer boundary, and an arbitrary number of interior
14/// cycles (i.e. holes).
15///
16/// `Face` has a defined orientation, a front and a back side. When faces are
17/// combined into [`Shell`]s, the face orientation defines what is inside and
18/// outside of the shell. This stands in contrast to [`Surface`], which has no
19/// defined orientation.
20///
21/// You can look at a `Face` from two directions: front and back. The winding of
22/// the exterior cycle will be clockwise or counter-clockwise, depending on your
23/// perspective. The front side of the face, is the side where from which the
24/// exterior cycle appear counter-clockwise.
25///
26/// Interior cycles must have the opposite winding of the exterior cycle,
27/// meaning on the front side of the face, they must appear clockwise. This
28/// means that all [`HalfEdge`]s that bound a `Face` have the interior of the
29/// face on their left side (on the face's front side).
30///
31/// [`HalfEdge`]: crate::objects::HalfEdge
32/// [`Shell`]: crate::objects::Shell
33#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
34pub struct Face {
35 surface: HandleWrapper<Surface>,
36 region: Handle<Region>,
37}
38
39impl Face {
40 /// Construct an instance of `Face`
41 pub fn new(surface: Handle<Surface>, region: Handle<Region>) -> Self {
42 Self {
43 surface: surface.into(),
44 region,
45 }
46 }
47
48 /// Access the surface of the face
49 pub fn surface(&self) -> &Handle<Surface> {
50 &self.surface
51 }
52
53 /// Access the region of the face
54 pub fn region(&self) -> &Handle<Region> {
55 &self.region
56 }
57
58 /// Determine handed-ness of the face's front-side coordinate system
59 ///
60 /// A face is defined on a surface, which has a coordinate system. Since
61 /// surfaces aren't considered to have an orientation, their coordinate
62 /// system can be considered to be left-handed or right-handed, depending on
63 /// which side of the surface you're looking at.
64 ///
65 /// Faces *do* have an orientation, meaning they have definite front and
66 /// back sides. The front side is the side, where the face's exterior cycle
67 /// is wound counter-clockwise.
68 pub fn coord_handedness(&self, geometry: &Geometry) -> Handedness {
69 match self.region.exterior().winding(geometry) {
70 Winding::Ccw => Handedness::RightHanded,
71 Winding::Cw => Handedness::LeftHanded,
72 }
73 }
74}
75
76/// The handedness of a face's coordinate system
77///
78/// See [`Face::coord_handedness`].
79#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
80pub enum Handedness {
81 /// The face's coordinate system is left-handed
82 LeftHanded,
83
84 /// The face's coordinate system is right-handed
85 RightHanded,
86}