fj_kernel/algorithms/intersect/
face_face.rs

1use fj_interop::ext::ArrayExt;
2use iter_fixed::IntoIteratorFixed;
3
4use crate::{geometry::curve::Curve, objects::Face};
5
6use super::{CurveFaceIntersection, SurfaceSurfaceIntersection};
7
8/// An intersection between two faces
9#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
10pub struct FaceFaceIntersection {
11    /// The intersection curves
12    ///
13    /// These curves correspond to the input faces, each being the local
14    /// representation of the intersection on the respective face's surface.
15    ///
16    /// They both represent the same global curve.
17    pub intersection_curves: [Curve; 2],
18
19    /// The interval of this intersection, in curve coordinates
20    ///
21    /// These curve coordinates apply to both intersection curves equally.
22    pub intersection_intervals: CurveFaceIntersection,
23}
24
25impl FaceFaceIntersection {
26    /// Compute the intersections between two faces
27    pub fn compute(faces: [&Face; 2]) -> Option<Self> {
28        let surfaces = faces.map(|face| face.surface().clone());
29
30        let intersection_curves =
31            match SurfaceSurfaceIntersection::compute(surfaces) {
32                Some(intersection) => intersection.intersection_curves,
33                None => return None,
34            };
35
36        let curve_face_intersections = intersection_curves
37            .each_ref_ext()
38            .into_iter_fixed()
39            .zip(faces)
40            .map(|(curve, face)| CurveFaceIntersection::compute(curve, face))
41            .collect::<[_; 2]>();
42
43        let intersection_intervals = {
44            let [a, b] = curve_face_intersections;
45            a.merge(&b)
46        };
47
48        if intersection_intervals.is_empty() {
49            return None;
50        }
51
52        Some(Self {
53            intersection_curves,
54            intersection_intervals,
55        })
56    }
57}
58
59#[cfg(test)]
60mod tests {
61    use pretty_assertions::assert_eq;
62
63    use crate::{
64        algorithms::intersect::CurveFaceIntersection,
65        geometry::curve::Curve,
66        objects::{Cycle, Face},
67        operations::{BuildCycle, BuildFace, Insert, UpdateFace},
68        services::Services,
69    };
70
71    use super::FaceFaceIntersection;
72
73    #[test]
74    fn compute_no_intersection() {
75        let mut services = Services::new();
76
77        #[rustfmt::skip]
78        let points = [
79            [1., 1.],
80            [2., 1.],
81            [2., 2.],
82            [1., 2.],
83        ];
84        let [a, b] = [
85            services.objects.surfaces.xy_plane(),
86            services.objects.surfaces.xz_plane(),
87        ]
88        .map(|surface| {
89            Face::unbound(surface, &mut services).update_exterior(|_| {
90                Cycle::polygon(points, &mut services).insert(&mut services)
91            })
92        });
93
94        let intersection = FaceFaceIntersection::compute([&a, &b]);
95        assert!(intersection.is_none());
96
97        services.only_validate([a, b]);
98    }
99
100    #[test]
101    fn compute_one_intersection() {
102        let mut services = Services::new();
103
104        #[rustfmt::skip]
105        let points = [
106            [-1., -1.],
107            [ 1., -1.],
108            [ 1.,  1.],
109            [-1.,  1.],
110        ];
111        let surfaces = [
112            services.objects.surfaces.xy_plane(),
113            services.objects.surfaces.xz_plane(),
114        ];
115        let [a, b] = surfaces.clone().map(|surface| {
116            Face::unbound(surface, &mut services).update_exterior(|_| {
117                Cycle::polygon(points, &mut services).insert(&mut services)
118            })
119        });
120
121        let intersection = FaceFaceIntersection::compute([&a, &b]);
122
123        let expected_curves = surfaces.map(|_| {
124            let (path, _) = Curve::line_from_points([[0., 0.], [1., 0.]]);
125            path
126        });
127        let expected_intervals =
128            CurveFaceIntersection::from_intervals([[[-1.], [1.]]]);
129        assert_eq!(
130            intersection,
131            Some(FaceFaceIntersection {
132                intersection_curves: expected_curves,
133                intersection_intervals: expected_intervals
134            })
135        );
136
137        services.only_validate([a, b]);
138    }
139}