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}