fj_kernel/algorithms/intersect/
curve_face.rs

1use std::vec;
2
3use fj_interop::ext::SliceExt;
4use fj_math::Point;
5
6use crate::{geometry::curve::Curve, objects::Face};
7
8use super::CurveEdgeIntersection;
9
10/// The intersections between a curve and a [`Face`], in curve coordinates
11#[derive(Clone, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
12pub struct CurveFaceIntersection {
13    /// The intervals where the curve and face intersect, in curve coordinates
14    pub intervals: Vec<CurveFaceIntersectionInterval>,
15}
16
17impl CurveFaceIntersection {
18    /// Create a new instance from the intersection intervals
19    ///
20    /// This method is useful for test code.
21    pub fn from_intervals(
22        intervals: impl IntoIterator<
23            Item = impl Into<CurveFaceIntersectionInterval>,
24        >,
25    ) -> Self {
26        let intervals = intervals.into_iter().map(Into::into).collect();
27        Self { intervals }
28    }
29
30    /// Compute the intersection
31    pub fn compute(curve: &Curve, face: &Face) -> Self {
32        let half_edges = face.all_cycles().flat_map(|cycle| cycle.half_edges());
33
34        let mut intersections = Vec::new();
35
36        for half_edge in half_edges {
37            let intersection = CurveEdgeIntersection::compute(curve, half_edge);
38
39            if let Some(intersection) = intersection {
40                match intersection {
41                    CurveEdgeIntersection::Point { point_on_curve } => {
42                        intersections.push(point_on_curve);
43                    }
44                    CurveEdgeIntersection::Coincident { points_on_curve } => {
45                        intersections.extend(points_on_curve);
46                    }
47                }
48            }
49        }
50
51        assert!(intersections.len() % 2 == 0);
52
53        intersections.sort();
54
55        let intervals = intersections
56            .as_slice()
57            .array_chunks_ext()
58            .map(|&[start, end]| CurveFaceIntersectionInterval { start, end })
59            .collect();
60
61        Self { intervals }
62    }
63
64    /// Merge this intersection list with another
65    ///
66    /// The merged list will contain all overlaps of the intervals from the two
67    /// other lists.
68    pub fn merge(&self, other: &Self) -> Self {
69        let mut self_intervals = self.intervals.iter().copied();
70        let mut other_interval = other.intervals.iter().copied();
71
72        let mut next_self = self_intervals.next();
73        let mut next_other = other_interval.next();
74
75        let mut intervals = Vec::new();
76
77        while let (Some(self_), Some(other)) = (next_self, next_other) {
78            // If we're starting another loop iteration, we have another
79            // interval available from both `self` and `other` each. Only if
80            // that's the case, is there a chance for an overlap.
81
82            // Build the overlap of the two next intervals, by comparing them.
83            // At this point we don't know yet, if this is a valid interval.
84            let overlap_start = self_.start.max(other.start);
85            let overlap_end = self_.end.min(other.end);
86
87            if overlap_start < overlap_end {
88                // This is indeed a valid overlap. Add it to our list of
89                // results.
90                intervals.push(CurveFaceIntersectionInterval {
91                    start: overlap_start,
92                    end: overlap_end,
93                });
94            }
95
96            // Only if the end of the overlap interval has overtaken one of the
97            // input ones are we done with it. An input interval that hasn't
98            // been overtaken by the overlap, could still overlap with another
99            // interval.
100            if self_.end <= overlap_end {
101                // Current interval from `self` has been overtaken. Let's grab
102                // the next one.
103                next_self = self_intervals.next();
104            }
105            if other.end <= overlap_end {
106                // Current interval from `other` has been overtaken. Let's grab
107                // the next one.
108                next_other = other_interval.next();
109            }
110        }
111
112        Self { intervals }
113    }
114
115    /// Indicate whether the intersection list is empty
116    pub fn is_empty(&self) -> bool {
117        self.intervals.is_empty()
118    }
119}
120
121impl IntoIterator for CurveFaceIntersection {
122    type Item = CurveFaceIntersectionInterval;
123    type IntoIter = vec::IntoIter<Self::Item>;
124
125    fn into_iter(self) -> Self::IntoIter {
126        self.intervals.into_iter()
127    }
128}
129
130/// An intersection between a curve and a face
131#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd)]
132pub struct CurveFaceIntersectionInterval {
133    /// The start of the intersection interval, in curve coordinates
134    pub start: Point<1>,
135
136    /// The end of the intersection interval, in curve coordinates
137    pub end: Point<1>,
138}
139
140impl<P> From<[P; 2]> for CurveFaceIntersectionInterval
141where
142    P: Into<Point<1>>,
143{
144    fn from(interval: [P; 2]) -> Self {
145        let [start, end] = interval.map(Into::into);
146        Self { start, end }
147    }
148}
149
150#[cfg(test)]
151mod tests {
152    use crate::{
153        geometry::curve::Curve,
154        objects::{Cycle, Face},
155        operations::{BuildCycle, BuildFace, Insert, UpdateFace},
156        services::Services,
157    };
158
159    use super::CurveFaceIntersection;
160
161    #[test]
162    fn compute() {
163        let mut services = Services::new();
164
165        let (curve, _) = Curve::line_from_points([[-3., 0.], [-2., 0.]]);
166
167        #[rustfmt::skip]
168        let exterior_points = [
169            [-2., -2.],
170            [ 2., -2.],
171            [ 2.,  2.],
172            [-2.,  2.],
173        ];
174        #[rustfmt::skip]
175        let interior_points = [
176            [-1., -1.],
177            [-1.,  1.],
178            [ 1.,  1.],
179            [ 1., -1.],
180        ];
181
182        let face =
183            Face::unbound(services.objects.surfaces.xy_plane(), &mut services)
184                .update_exterior(|_| {
185                    Cycle::polygon(exterior_points, &mut services)
186                        .insert(&mut services)
187                })
188                .add_interiors([Cycle::polygon(
189                    interior_points,
190                    &mut services,
191                )
192                .insert(&mut services)]);
193
194        let expected =
195            CurveFaceIntersection::from_intervals([[[1.], [2.]], [[4.], [5.]]]);
196        assert_eq!(CurveFaceIntersection::compute(&curve, &face), expected);
197
198        services.only_validate(face);
199    }
200
201    #[test]
202    fn merge() {
203        let a = CurveFaceIntersection::from_intervals([
204            [[0.], [1.]],   // 1: `a` and `b` are equal
205            [[2.], [5.]],   // 2: `a` contains `b`
206            [[7.], [8.]],   // 3: `b` contains `a`
207            [[9.], [11.]],  // 4: overlap; `a` is left
208            [[14.], [16.]], // 5: overlap; `a` is right
209            [[18.], [21.]], // 6: one of `a` partially overlaps two of `b`
210            [[23.], [25.]], // 7: two of `a` partially overlap one of `b`
211            [[26.], [28.]], // 7
212            [[31.], [35.]], // 8: partial/complete: one of `a`, two of `b`;
213            [[36.], [38.]], // 9: partial/complete: two of `a`, one of `b`
214            [[39.], [40.]], // 9
215            [[41.], [45.]], // 10: complete/partial: one of `a`, two of `b`
216            [[48.], [49.]], // 11: complete/partial: two of `a`, one of `b`
217            [[50.], [52.]], // 11
218            [[53.], [58.]], // 12: one of `a` overlaps two of `b` completely
219            [[60.], [61.]], // 13: one of `b` overlaps two of `a` completely
220            [[62.], [63.]], // 13
221            [[65.], [66.]], // 14: one of `a` with no overlap in `b`
222        ]);
223        let b = CurveFaceIntersection::from_intervals([
224            [[0.], [1.]],   // 1
225            [[3.], [4.]],   // 2
226            [[6.], [9.]],   // 3
227            [[10.], [12.]], // 4
228            [[13.], [15.]], // 5
229            [[17.], [19.]], // 6
230            [[20.], [22.]], // 6
231            [[24.], [27.]], // 7
232            [[30.], [32.]], // 8
233            [[33.], [34.]], // 8
234            [[37.], [41.]], // 9
235            [[42.], [43.]], // 10
236            [[44.], [46.]], // 10
237            [[47.], [51.]], // 11
238            [[54.], [55.]], // 12
239            [[56.], [57.]], // 12
240            [[59.], [64.]], // 13
241        ]);
242
243        let merged = a.merge(&b);
244
245        let expected = CurveFaceIntersection::from_intervals([
246            [[0.], [1.]],   // 1
247            [[3.], [4.]],   // 2
248            [[7.], [8.]],   // 3
249            [[10.], [11.]], // 4
250            [[14.], [15.]], // 5
251            [[18.], [19.]], // 6
252            [[20.], [21.]], // 6
253            [[24.], [25.]], // 7
254            [[26.], [27.]], // 7
255            [[31.], [32.]], // 8
256            [[33.], [34.]], // 8
257            [[37.], [38.]], // 9
258            [[39.], [40.]], // 9
259            [[42.], [43.]], // 10
260            [[44.], [45.]], // 10
261            [[48.], [49.]], // 11
262            [[50.], [51.]], // 11
263            [[54.], [55.]], // 12
264            [[56.], [57.]], // 12
265            [[60.], [61.]], // 13
266            [[62.], [63.]], // 13
267        ]);
268        assert_eq!(merged, expected);
269    }
270}