Struct fj_kernel::objects::Face

source ·
pub struct Face { /* private fields */ }
Expand description

A face of a shape

A Face is a bounded area of a Surface, the Surface itself being an infinite 2-dimensional object in 3D space. Faces are bound by one exterior cycle, which defines the outer boundary, and an arbitrary number of interior cycles (i.e. holes).

Face has a defined orientation, a front and a back side. When faces are combined into Shells, the face orientation defines what is inside and outside of the shell. This stands in contrast to Surface, which has no defined orientation.

You can look at a Face from two directions: front and back. The winding of the exterior cycle will be clockwise or counter-clockwise, depending on your perspective. The front side of the face, is the side where from which the exterior cycle appear counter-clockwise.

Interior cycles must have the opposite winding of the exterior cycle, meaning on the front side of the face, they must appear clockwise. This means that all HalfEdges that bound a Face have the interior of the face on their left side (on the face’s front side).

Implementations§

Construct an instance of Face

Examples found in repository?
src/partial/objects/face.rs (line 44)
38
39
40
41
42
43
44
45
    fn build(self, objects: &mut Service<Objects>) -> Self::Full {
        let exterior = self.exterior.build(objects);
        let interiors =
            self.interiors.into_iter().map(|cycle| cycle.build(objects));
        let color = self.color.unwrap_or_default();

        Face::new(exterior, interiors, color)
    }
More examples
Hide additional examples
src/algorithms/transform/face.rs (line 28)
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    fn transform_with_cache(
        self,
        transform: &Transform,
        objects: &mut Service<Objects>,
        cache: &mut TransformCache,
    ) -> Self {
        // Color does not need to be transformed.
        let color = self.color();

        let exterior = self
            .exterior()
            .clone()
            .transform_with_cache(transform, objects, cache);
        let interiors = self.interiors().cloned().map(|interior| {
            interior.transform_with_cache(transform, objects, cache)
        });

        Self::new(exterior, interiors, color)
    }

Access the surface of the face

Examples found in repository?
src/iter.rs (line 171)
170
171
172
173
174
175
176
177
178
    fn referenced_objects(&'r self) -> Vec<&'r dyn ObjectIters> {
        let mut objects = vec![self.surface() as &dyn ObjectIters];

        for cycle in self.all_cycles() {
            objects.push(cycle);
        }

        objects
    }
More examples
Hide additional examples
src/validate/face.rs (line 65)
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
    fn check_surface_identity(face: &Face) -> Result<(), Self> {
        let surface = face.surface();

        for interior in face.interiors() {
            if surface.id() != interior.surface().id() {
                return Err(Self::SurfaceMismatch {
                    surface: surface.clone(),
                    interior: interior.clone(),
                    face: face.clone(),
                });
            }
        }

        Ok(())
    }
src/algorithms/intersect/face_face.rs (line 35)
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
    pub fn compute(
        faces: [&Face; 2],
        objects: &mut Service<Objects>,
    ) -> Option<Self> {
        let surfaces = faces.map(|face| face.surface().clone());

        let intersection_curves =
            match SurfaceSurfaceIntersection::compute(surfaces, objects) {
                Some(intersection) => intersection.intersection_curves,
                None => return None,
            };

        let curve_face_intersections = intersection_curves
            .each_ref_ext()
            .into_iter_fixed()
            .zip(faces)
            .map(|(curve, face)| CurveFaceIntersection::compute(curve, face))
            .collect::<[_; 2]>();

        let intersection_intervals = {
            let [a, b] = curve_face_intersections;
            a.merge(&b)
        };

        if intersection_intervals.is_empty() {
            return None;
        }

        Some(Self {
            intersection_curves,
            intersection_intervals,
        })
    }
src/algorithms/sweep/face.rs (line 29)
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
    fn sweep_with_cache(
        self,
        path: impl Into<Vector<3>>,
        cache: &mut SweepCache,
        objects: &mut Service<Objects>,
    ) -> Self::Swept {
        let path = path.into();

        let mut faces = Vec::new();

        let is_negative_sweep = {
            let u = match self.surface().geometry().u {
                GlobalPath::Circle(_) => todo!(
                    "Sweeping from faces defined in round surfaces is not \
                    supported"
                ),
                GlobalPath::Line(line) => line.direction(),
            };
            let v = self.surface().geometry().v;

            let normal = u.cross(&v);

            normal.dot(&path) < Scalar::ZERO
        };

        let bottom_face = {
            if is_negative_sweep {
                self.clone()
            } else {
                self.clone().reverse(objects)
            }
        };
        faces.push(bottom_face);

        let top_face = {
            let mut face = self.clone().translate(path, objects);

            if is_negative_sweep {
                face = face.reverse(objects);
            };

            face
        };
        faces.push(top_face);

        // Generate side faces
        for cycle in self.all_cycles() {
            for half_edge in cycle.half_edges() {
                let half_edge = if is_negative_sweep {
                    half_edge.clone().reverse(objects)
                } else {
                    half_edge.clone()
                };

                let face = (half_edge, self.color())
                    .sweep_with_cache(path, cache, objects);

                faces.push(face);
            }
        }

        let faces = faces.into_iter().map(Partial::from).collect();
        PartialShell { faces }.build(objects).insert(objects)
    }
src/algorithms/intersect/ray_face.rs (line 20)
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
    fn intersect(self) -> Option<Self::Intersection> {
        let (ray, face) = self;

        let plane = match face.surface().geometry().u {
            GlobalPath::Circle(_) => todo!(
                "Casting a ray against a swept circle is not supported yet"
            ),
            GlobalPath::Line(line) => Plane::from_parametric(
                line.origin(),
                line.direction(),
                face.surface().geometry().v,
            ),
        };

        if plane.is_parallel_to_vector(&ray.direction()) {
            let a = plane.origin();
            let b = plane.origin() + plane.u();
            let c = plane.origin() + plane.v();
            let d = ray.origin;

            let [a, b, c, d] = [a, b, c, d]
                .map(|point| [point.x, point.y, point.z])
                .map(|point| point.map(Scalar::into_f64));

            if robust_predicates::orient3d(&a, &b, &c, &d) == 0. {
                return Some(RayFaceIntersection::RayHitsFaceAndAreParallel);
            } else {
                return None;
            }
        }

        // The pattern in this assertion resembles `ax*by = ay*bx`, which holds
        // true if the vectors `a = (ax, ay)` and `b = (bx, by)` are parallel.
        //
        // We're looking at the plane's direction vectors here, but we're
        // ignoring their x-components. By doing that, we're essentially
        // projecting those vectors into the yz-plane.
        //
        // This means that the following assertion verifies that the projections
        // of the plane's direction vectors into the yz-plane are not parallel.
        // If they were, then the plane could only be parallel to the x-axis,
        // and thus our ray.
        //
        // We already handled the case of the ray and plane being parallel
        // above. The following assertion should thus never be triggered.
        assert_ne!(
            plane.u().y * plane.v().z,
            plane.u().z * plane.v().y,
            "Plane and ray are parallel; should have been ruled out previously"
        );

        // Let's figure out the intersection between the ray and the plane.
        let (t, u, v) = {
            // The following math would get *very* unwieldy with those
            // full-length variable names. Let's define some short-hands.
            let orx = ray.origin.x;
            let ory = ray.origin.y;
            let orz = ray.origin.z;
            let opx = plane.origin().x;
            let opy = plane.origin().y;
            let opz = plane.origin().z;
            let d1x = plane.u().x;
            let d1y = plane.u().y;
            let d1z = plane.u().z;
            let d2x = plane.v().x;
            let d2y = plane.v().y;
            let d2z = plane.v().z;

            // Let's figure out where the intersection between the ray and the
            // plane is. By equating the parametric equations of the ray and the
            // plane, we get a vector equation, which in turn gives us a system
            // of three equations with three unknowns: `t` (for the ray) and
            // `u`/`v` (for the plane).
            //
            // Since the ray's direction vector is `(1, 0, 0)`, it works out
            // such that `t` is not in the equations for y and z, meaning we can
            // solve those equations for `u` and `v` independently.
            //
            // By doing some math, we get the following solutions:
            let v = (d1y * (orz - opz) + (opy - ory) * d1z)
                / (d1y * d2z - d2y * d1z);
            let u = (ory - opy - d2y * v) / d1y;
            let t = opx - orx + d1x * u + d2x * v;

            (t, u, v)
        };

        if t < Scalar::ZERO {
            // Ray points away from plane.
            return None;
        }

        let point = Point::from([u, v]);
        let intersection = match (face, &point).intersect()? {
            FacePointIntersection::PointIsInsideFace => {
                RayFaceIntersection::RayHitsFace
            }
            FacePointIntersection::PointIsOnEdge(edge) => {
                RayFaceIntersection::RayHitsEdge(edge)
            }
            FacePointIntersection::PointIsOnVertex(vertex) => {
                RayFaceIntersection::RayHitsVertex(vertex)
            }
        };

        Some(intersection)
    }

Access the cycle that bounds the face on the outside

Examples found in repository?
src/objects/full/face.rs (line 60)
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
    pub fn surface(&self) -> &Handle<Surface> {
        self.exterior().surface()
    }

    /// Access the cycle that bounds the face on the outside
    pub fn exterior(&self) -> &Handle<Cycle> {
        &self.exterior
    }

    /// Access the cycles that bound the face on the inside
    ///
    /// Each of these cycles defines a hole in the face.
    pub fn interiors(&self) -> impl Iterator<Item = &Handle<Cycle>> + '_ {
        self.interiors.iter()
    }

    /// Access all cycles of the face
    pub fn all_cycles(&self) -> impl Iterator<Item = &Handle<Cycle>> + '_ {
        [self.exterior()].into_iter().chain(self.interiors())
    }

    /// Access the color of the face
    pub fn color(&self) -> Color {
        self.color
    }

    /// Determine handed-ness of the face's front-side coordinate system
    ///
    /// A face is defined on a surface, which has a coordinate system. Since
    /// surfaces aren't considered to have an orientation, their coordinate
    /// system can be considered to be left-handed or right-handed, depending on
    /// which side of the surface you're looking at.
    ///
    /// Faces *do* have an orientation, meaning they have definite front and
    /// back sides. The front side is the side, where the face's exterior cycle
    /// is wound counter-clockwise.
    pub fn coord_handedness(&self) -> Handedness {
        match self.exterior().winding() {
            Winding::Ccw => Handedness::RightHanded,
            Winding::Cw => Handedness::LeftHanded,
        }
    }
More examples
Hide additional examples
src/partial/objects/face.rs (line 29)
27
28
29
30
31
32
33
34
35
36
    fn from_full(face: &Self::Full, cache: &mut FullToPartialCache) -> Self {
        Self {
            exterior: Partial::from_full(face.exterior().clone(), cache),
            interiors: face
                .interiors()
                .map(|cycle| Partial::from_full(cycle.clone(), cache))
                .collect(),
            color: Some(face.color()),
        }
    }
src/algorithms/transform/face.rs (line 21)
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    fn transform_with_cache(
        self,
        transform: &Transform,
        objects: &mut Service<Objects>,
        cache: &mut TransformCache,
    ) -> Self {
        // Color does not need to be transformed.
        let color = self.color();

        let exterior = self
            .exterior()
            .clone()
            .transform_with_cache(transform, objects, cache);
        let interiors = self.interiors().cloned().map(|interior| {
            interior.transform_with_cache(transform, objects, cache)
        });

        Self::new(exterior, interiors, color)
    }
src/algorithms/reverse/face.rs (line 16)
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    fn reverse(self, objects: &mut Service<Objects>) -> Self {
        let mut cache = FullToPartialCache::default();

        let exterior = Partial::from_full(
            self.exterior().clone().reverse(objects),
            &mut cache,
        );
        let interiors = self
            .interiors()
            .map(|cycle| {
                Partial::from_full(cycle.clone().reverse(objects), &mut cache)
            })
            .collect::<Vec<_>>();

        let face = PartialFace {
            exterior,
            interiors,
            color: Some(self.color()),
        };
        face.build(objects).insert(objects)
    }
src/validate/face.rs (line 81)
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
    fn check_interior_winding(face: &Face) -> Result<(), Self> {
        let exterior_winding = face.exterior().winding();

        for interior in face.interiors() {
            let interior_winding = interior.winding();

            if exterior_winding == interior_winding {
                return Err(Self::InvalidInteriorWinding {
                    exterior_winding,
                    interior_winding,
                    face: face.clone(),
                });
            }
            assert_ne!(
                exterior_winding,
                interior.winding(),
                "Interior cycles must have opposite winding of exterior cycle"
            );
        }

        Ok(())
    }
src/algorithms/approx/face.rs (line 95)
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
    fn approx_with_cache(
        self,
        tolerance: impl Into<Tolerance>,
        cache: &mut Self::Cache,
    ) -> Self::Approximation {
        let tolerance = tolerance.into();

        // Curved faces whose curvature is not fully defined by their edges
        // are not supported yet. For that reason, we can fully ignore `face`'s
        // `surface` field and just pass the edges to `Self::for_edges`.
        //
        // An example of a curved face that is supported, is the cylinder. Its
        // curvature is fully defined be the edges (circles) that border it. The
        // circle approximations are sufficient to triangulate the surface.
        //
        // An example of a curved face that is currently not supported, and thus
        // doesn't need to be handled here, is a sphere. A spherical face would
        // would need to provide its own approximation, as the edges that bound
        // it have nothing to do with its curvature.

        let exterior = self.exterior().approx_with_cache(tolerance, cache);

        let mut interiors = BTreeSet::new();
        for cycle in self.interiors() {
            let cycle = cycle.approx_with_cache(tolerance, cache);
            interiors.insert(cycle);
        }

        FaceApprox {
            exterior,
            interiors,
            color: self.color(),
            coord_handedness: self.coord_handedness(),
        }
    }

Access the cycles that bound the face on the inside

Each of these cycles defines a hole in the face.

Examples found in repository?
src/objects/full/face.rs (line 77)
76
77
78
    pub fn all_cycles(&self) -> impl Iterator<Item = &Handle<Cycle>> + '_ {
        [self.exterior()].into_iter().chain(self.interiors())
    }
More examples
Hide additional examples
src/partial/objects/face.rs (line 31)
27
28
29
30
31
32
33
34
35
36
    fn from_full(face: &Self::Full, cache: &mut FullToPartialCache) -> Self {
        Self {
            exterior: Partial::from_full(face.exterior().clone(), cache),
            interiors: face
                .interiors()
                .map(|cycle| Partial::from_full(cycle.clone(), cache))
                .collect(),
            color: Some(face.color()),
        }
    }
src/validate/face.rs (line 67)
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
    fn check_surface_identity(face: &Face) -> Result<(), Self> {
        let surface = face.surface();

        for interior in face.interiors() {
            if surface.id() != interior.surface().id() {
                return Err(Self::SurfaceMismatch {
                    surface: surface.clone(),
                    interior: interior.clone(),
                    face: face.clone(),
                });
            }
        }

        Ok(())
    }

    fn check_interior_winding(face: &Face) -> Result<(), Self> {
        let exterior_winding = face.exterior().winding();

        for interior in face.interiors() {
            let interior_winding = interior.winding();

            if exterior_winding == interior_winding {
                return Err(Self::InvalidInteriorWinding {
                    exterior_winding,
                    interior_winding,
                    face: face.clone(),
                });
            }
            assert_ne!(
                exterior_winding,
                interior.winding(),
                "Interior cycles must have opposite winding of exterior cycle"
            );
        }

        Ok(())
    }
src/algorithms/transform/face.rs (line 24)
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    fn transform_with_cache(
        self,
        transform: &Transform,
        objects: &mut Service<Objects>,
        cache: &mut TransformCache,
    ) -> Self {
        // Color does not need to be transformed.
        let color = self.color();

        let exterior = self
            .exterior()
            .clone()
            .transform_with_cache(transform, objects, cache);
        let interiors = self.interiors().cloned().map(|interior| {
            interior.transform_with_cache(transform, objects, cache)
        });

        Self::new(exterior, interiors, color)
    }
src/algorithms/reverse/face.rs (line 20)
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    fn reverse(self, objects: &mut Service<Objects>) -> Self {
        let mut cache = FullToPartialCache::default();

        let exterior = Partial::from_full(
            self.exterior().clone().reverse(objects),
            &mut cache,
        );
        let interiors = self
            .interiors()
            .map(|cycle| {
                Partial::from_full(cycle.clone().reverse(objects), &mut cache)
            })
            .collect::<Vec<_>>();

        let face = PartialFace {
            exterior,
            interiors,
            color: Some(self.color()),
        };
        face.build(objects).insert(objects)
    }
src/algorithms/approx/face.rs (line 98)
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
    fn approx_with_cache(
        self,
        tolerance: impl Into<Tolerance>,
        cache: &mut Self::Cache,
    ) -> Self::Approximation {
        let tolerance = tolerance.into();

        // Curved faces whose curvature is not fully defined by their edges
        // are not supported yet. For that reason, we can fully ignore `face`'s
        // `surface` field and just pass the edges to `Self::for_edges`.
        //
        // An example of a curved face that is supported, is the cylinder. Its
        // curvature is fully defined be the edges (circles) that border it. The
        // circle approximations are sufficient to triangulate the surface.
        //
        // An example of a curved face that is currently not supported, and thus
        // doesn't need to be handled here, is a sphere. A spherical face would
        // would need to provide its own approximation, as the edges that bound
        // it have nothing to do with its curvature.

        let exterior = self.exterior().approx_with_cache(tolerance, cache);

        let mut interiors = BTreeSet::new();
        for cycle in self.interiors() {
            let cycle = cycle.approx_with_cache(tolerance, cache);
            interiors.insert(cycle);
        }

        FaceApprox {
            exterior,
            interiors,
            color: self.color(),
            coord_handedness: self.coord_handedness(),
        }
    }

Access all cycles of the face

Examples found in repository?
src/iter.rs (line 173)
170
171
172
173
174
175
176
177
178
    fn referenced_objects(&'r self) -> Vec<&'r dyn ObjectIters> {
        let mut objects = vec![self.surface() as &dyn ObjectIters];

        for cycle in self.all_cycles() {
            objects.push(cycle);
        }

        objects
    }
More examples
Hide additional examples
src/algorithms/intersect/curve_face.rs (line 32)
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
    pub fn compute(curve: &Curve, face: &Face) -> Self {
        let half_edges = face.all_cycles().flat_map(|cycle| cycle.half_edges());

        let mut intersections = Vec::new();

        for half_edge in half_edges {
            let intersection = CurveEdgeIntersection::compute(curve, half_edge);

            if let Some(intersection) = intersection {
                match intersection {
                    CurveEdgeIntersection::Point { point_on_curve } => {
                        intersections.push(point_on_curve);
                    }
                    CurveEdgeIntersection::Coincident { points_on_curve } => {
                        intersections.extend(points_on_curve);
                    }
                }
            }
        }

        assert!(intersections.len() % 2 == 0);

        intersections.sort();

        let intervals = intersections
            .as_slice()
            .array_chunks_ext()
            .map(|&[start, end]| CurveFaceIntersectionInterval { start, end })
            .collect();

        Self { intervals }
    }
src/algorithms/sweep/face.rs (line 64)
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
    fn sweep_with_cache(
        self,
        path: impl Into<Vector<3>>,
        cache: &mut SweepCache,
        objects: &mut Service<Objects>,
    ) -> Self::Swept {
        let path = path.into();

        let mut faces = Vec::new();

        let is_negative_sweep = {
            let u = match self.surface().geometry().u {
                GlobalPath::Circle(_) => todo!(
                    "Sweeping from faces defined in round surfaces is not \
                    supported"
                ),
                GlobalPath::Line(line) => line.direction(),
            };
            let v = self.surface().geometry().v;

            let normal = u.cross(&v);

            normal.dot(&path) < Scalar::ZERO
        };

        let bottom_face = {
            if is_negative_sweep {
                self.clone()
            } else {
                self.clone().reverse(objects)
            }
        };
        faces.push(bottom_face);

        let top_face = {
            let mut face = self.clone().translate(path, objects);

            if is_negative_sweep {
                face = face.reverse(objects);
            };

            face
        };
        faces.push(top_face);

        // Generate side faces
        for cycle in self.all_cycles() {
            for half_edge in cycle.half_edges() {
                let half_edge = if is_negative_sweep {
                    half_edge.clone().reverse(objects)
                } else {
                    half_edge.clone()
                };

                let face = (half_edge, self.color())
                    .sweep_with_cache(path, cache, objects);

                faces.push(face);
            }
        }

        let faces = faces.into_iter().map(Partial::from).collect();
        PartialShell { faces }.build(objects).insert(objects)
    }
src/algorithms/intersect/face_point.rs (line 24)
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
    fn intersect(self) -> Option<Self::Intersection> {
        let (face, point) = self;

        let ray = HorizontalRayToTheRight { origin: *point };

        let mut num_hits = 0;

        for cycle in face.all_cycles() {
            // We need to properly detect the ray passing the boundary at the
            // "seam" of the polygon, i.e. the vertex between the last and the
            // first segment. The logic in the loop properly takes care of that,
            // as long as we initialize the `previous_hit` variable with the
            // result of the last segment.
            let mut previous_hit = cycle
                .half_edges()
                .last()
                .cloned()
                .and_then(|edge| (&ray, &edge).intersect());

            for half_edge in cycle.half_edges() {
                let hit = (&ray, half_edge).intersect();

                let count_hit = match (hit, previous_hit) {
                    (
                        Some(RaySegmentIntersection::RayStartsOnSegment),
                        _,
                    ) => {
                        // If the ray starts on the boundary of the face,
                        // there's nothing to else check.
                        return Some(FacePointIntersection::PointIsOnEdge(
                            half_edge.clone()
                        ));
                    }
                    (Some(RaySegmentIntersection::RayStartsOnOnFirstVertex), _) => {
                        let vertex = half_edge.vertices()[0].clone();
                        return Some(
                            FacePointIntersection::PointIsOnVertex(vertex)
                        );
                    }
                    (Some(RaySegmentIntersection::RayStartsOnSecondVertex), _) => {
                        let vertex = half_edge.vertices()[1].clone();
                        return Some(
                            FacePointIntersection::PointIsOnVertex(vertex)
                        );
                    }
                    (Some(RaySegmentIntersection::RayHitsSegment), _) => {
                        // We're hitting a segment right-on. Clear case.
                        true
                    }
                    (
                        Some(RaySegmentIntersection::RayHitsUpperVertex),
                        Some(RaySegmentIntersection::RayHitsLowerVertex),
                    )
                    | (
                        Some(RaySegmentIntersection::RayHitsLowerVertex),
                        Some(RaySegmentIntersection::RayHitsUpperVertex),
                    ) => {
                        // If we're hitting a vertex, only count it if we've hit
                        // the other kind of vertex right before.
                        //
                        // That means, we're passing through the polygon
                        // boundary at where two edges touch. Depending on the
                        // order in which edges are checked, we're seeing this
                        // as a hit to one edge's lower/upper vertex, then the
                        // other edge's opposite vertex.
                        //
                        // If we're seeing two of the same vertices in a row,
                        // we're not actually passing through the polygon
                        // boundary. Then we're just touching a vertex without
                        // passing through anything.
                        true
                    }
                    (Some(RaySegmentIntersection::RayHitsSegmentAndAreParallel), _) => {
                        // A parallel edge must be completely ignored. Its
                        // presence won't change anything, so we can treat it as
                        // if it wasn't there, and its neighbors were connected
                        // to each other.
                        continue;
                    }
                    _ => {
                        // Any other case is not a valid hit.
                        false
                    }
                };

                if count_hit {
                    num_hits += 1;
                }

                previous_hit = hit;
            }
        }

        if num_hits % 2 == 1 {
            Some(FacePointIntersection::PointIsInsideFace)
        } else {
            None
        }
    }

Access the color of the face

Examples found in repository?
src/partial/objects/face.rs (line 34)
27
28
29
30
31
32
33
34
35
36
    fn from_full(face: &Self::Full, cache: &mut FullToPartialCache) -> Self {
        Self {
            exterior: Partial::from_full(face.exterior().clone(), cache),
            interiors: face
                .interiors()
                .map(|cycle| Partial::from_full(cycle.clone(), cache))
                .collect(),
            color: Some(face.color()),
        }
    }
More examples
Hide additional examples
src/algorithms/transform/face.rs (line 18)
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
    fn transform_with_cache(
        self,
        transform: &Transform,
        objects: &mut Service<Objects>,
        cache: &mut TransformCache,
    ) -> Self {
        // Color does not need to be transformed.
        let color = self.color();

        let exterior = self
            .exterior()
            .clone()
            .transform_with_cache(transform, objects, cache);
        let interiors = self.interiors().cloned().map(|interior| {
            interior.transform_with_cache(transform, objects, cache)
        });

        Self::new(exterior, interiors, color)
    }
src/algorithms/reverse/face.rs (line 29)
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
    fn reverse(self, objects: &mut Service<Objects>) -> Self {
        let mut cache = FullToPartialCache::default();

        let exterior = Partial::from_full(
            self.exterior().clone().reverse(objects),
            &mut cache,
        );
        let interiors = self
            .interiors()
            .map(|cycle| {
                Partial::from_full(cycle.clone().reverse(objects), &mut cache)
            })
            .collect::<Vec<_>>();

        let face = PartialFace {
            exterior,
            interiors,
            color: Some(self.color()),
        };
        face.build(objects).insert(objects)
    }
src/algorithms/approx/face.rs (line 106)
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
    fn approx_with_cache(
        self,
        tolerance: impl Into<Tolerance>,
        cache: &mut Self::Cache,
    ) -> Self::Approximation {
        let tolerance = tolerance.into();

        // Curved faces whose curvature is not fully defined by their edges
        // are not supported yet. For that reason, we can fully ignore `face`'s
        // `surface` field and just pass the edges to `Self::for_edges`.
        //
        // An example of a curved face that is supported, is the cylinder. Its
        // curvature is fully defined be the edges (circles) that border it. The
        // circle approximations are sufficient to triangulate the surface.
        //
        // An example of a curved face that is currently not supported, and thus
        // doesn't need to be handled here, is a sphere. A spherical face would
        // would need to provide its own approximation, as the edges that bound
        // it have nothing to do with its curvature.

        let exterior = self.exterior().approx_with_cache(tolerance, cache);

        let mut interiors = BTreeSet::new();
        for cycle in self.interiors() {
            let cycle = cycle.approx_with_cache(tolerance, cache);
            interiors.insert(cycle);
        }

        FaceApprox {
            exterior,
            interiors,
            color: self.color(),
            coord_handedness: self.coord_handedness(),
        }
    }
src/algorithms/sweep/face.rs (line 72)
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
    fn sweep_with_cache(
        self,
        path: impl Into<Vector<3>>,
        cache: &mut SweepCache,
        objects: &mut Service<Objects>,
    ) -> Self::Swept {
        let path = path.into();

        let mut faces = Vec::new();

        let is_negative_sweep = {
            let u = match self.surface().geometry().u {
                GlobalPath::Circle(_) => todo!(
                    "Sweeping from faces defined in round surfaces is not \
                    supported"
                ),
                GlobalPath::Line(line) => line.direction(),
            };
            let v = self.surface().geometry().v;

            let normal = u.cross(&v);

            normal.dot(&path) < Scalar::ZERO
        };

        let bottom_face = {
            if is_negative_sweep {
                self.clone()
            } else {
                self.clone().reverse(objects)
            }
        };
        faces.push(bottom_face);

        let top_face = {
            let mut face = self.clone().translate(path, objects);

            if is_negative_sweep {
                face = face.reverse(objects);
            };

            face
        };
        faces.push(top_face);

        // Generate side faces
        for cycle in self.all_cycles() {
            for half_edge in cycle.half_edges() {
                let half_edge = if is_negative_sweep {
                    half_edge.clone().reverse(objects)
                } else {
                    half_edge.clone()
                };

                let face = (half_edge, self.color())
                    .sweep_with_cache(path, cache, objects);

                faces.push(face);
            }
        }

        let faces = faces.into_iter().map(Partial::from).collect();
        PartialShell { faces }.build(objects).insert(objects)
    }

Determine handed-ness of the face’s front-side coordinate system

A face is defined on a surface, which has a coordinate system. Since surfaces aren’t considered to have an orientation, their coordinate system can be considered to be left-handed or right-handed, depending on which side of the surface you’re looking at.

Faces do have an orientation, meaning they have definite front and back sides. The front side is the side, where the face’s exterior cycle is wound counter-clockwise.

Examples found in repository?
src/algorithms/approx/face.rs (line 107)
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
    fn approx_with_cache(
        self,
        tolerance: impl Into<Tolerance>,
        cache: &mut Self::Cache,
    ) -> Self::Approximation {
        let tolerance = tolerance.into();

        // Curved faces whose curvature is not fully defined by their edges
        // are not supported yet. For that reason, we can fully ignore `face`'s
        // `surface` field and just pass the edges to `Self::for_edges`.
        //
        // An example of a curved face that is supported, is the cylinder. Its
        // curvature is fully defined be the edges (circles) that border it. The
        // circle approximations are sufficient to triangulate the surface.
        //
        // An example of a curved face that is currently not supported, and thus
        // doesn't need to be handled here, is a sphere. A spherical face would
        // would need to provide its own approximation, as the edges that bound
        // it have nothing to do with its curvature.

        let exterior = self.exterior().approx_with_cache(tolerance, cache);

        let mut interiors = BTreeSet::new();
        for cycle in self.interiors() {
            let cycle = cycle.approx_with_cache(tolerance, cache);
            interiors.insert(cycle);
        }

        FaceApprox {
            exterior,
            interiors,
            color: self.color(),
            coord_handedness: self.coord_handedness(),
        }
    }

Trait Implementations§

The approximation of the object
The cache used to cache approximation results
Approximate the object, using the provided cache Read more
Approximate the object Read more
Returns a copy of the value. Read more
Performs copy-assignment from source. Read more
Formats the value using the given formatter. Read more
Converts to this type from the input type.
The type representing the partial variant of this object
Feeds this value into the given Hasher. Read more
Feeds a slice of this type into the given Hasher. Read more
Insert the object into its respective store
This method returns an Ordering between self and other. Read more
Compares and returns the maximum of two values. Read more
Compares and returns the minimum of two values. Read more
Restrict a value to a certain interval. Read more
This method tests for self and other values to be equal, and is used by ==.
This method tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
This method returns an ordering between self and other values if one exists. Read more
This method tests less than (for self and other) and is used by the < operator. Read more
This method tests less than or equal to (for self and other) and is used by the <= operator. Read more
This method tests greater than (for self and other) and is used by the > operator. Read more
This method tests greater than or equal to (for self and other) and is used by the >= operator. Read more
Transform the object using the provided cache
Transform the object
Translate the object Read more
Rotate the object Read more
The error that validation of the implementing type can result in
Validate the object
Validate the object using default configuration

Auto Trait Implementations§

Blanket Implementations§

Gets the TypeId of self. Read more
Immutably borrows from an owned value. Read more
Mutably borrows from an owned value. Read more
Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Convert Arc<Trait> (where Trait: Downcast) to Arc<Any>. Arc<Any> can then be further downcast into Arc<ConcreteType> where ConcreteType implements Trait.

Returns the argument unchanged.

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Should always be Self
The inverse inclusion map: attempts to construct self from the equivalent element of its superset. Read more
Checks if self is actually part of its subset T (and can be converted to it).
Use with care! Same as self.to_subset but without any property checks. Always succeeds.
The inclusion map: converts self to the equivalent element of its superset.
The resulting type after obtaining ownership.
Creates owned data from borrowed data, usually by cloning. Read more
Uses borrowed data to replace owned data, usually by cloning. Read more
The type returned in the event of a conversion error.
Performs the conversion.
The type returned in the event of a conversion error.
Performs the conversion.