parry3d/transformation/
polygon_intersection.rs

1use alloc::{vec, vec::Vec};
2use log::error;
3use na::Point2;
4use ordered_float::OrderedFloat;
5
6use crate::math::Real;
7use crate::shape::{SegmentPointLocation, Triangle, TriangleOrientation};
8use crate::utils::hashmap::HashMap;
9use crate::utils::{self, SegmentsIntersection};
10
11/// Tolerances for polygon intersection algorithms.
12///
13/// These tolerances control how the intersection algorithm handles numerical precision
14/// issues when determining geometric relationships between points, lines, and polygons.
15///
16/// # Examples
17///
18/// ```
19/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
20/// # use parry2d::transformation::PolygonIntersectionTolerances;
21/// // Use default tolerances (recommended for most cases)
22/// let default_tol = PolygonIntersectionTolerances::default();
23///
24/// // Or create custom tolerances for special cases
25/// let custom_tol = PolygonIntersectionTolerances {
26///     collinearity_epsilon: 1.0e-5,
27/// };
28/// # }
29/// ```
30#[derive(Copy, Clone, PartialEq, Debug)]
31pub struct PolygonIntersectionTolerances {
32    /// The epsilon given to [`Triangle::orientation2d`] for detecting if three points are collinear.
33    ///
34    /// Three points forming a triangle with an area smaller than this value are considered collinear.
35    pub collinearity_epsilon: Real,
36}
37
38impl Default for PolygonIntersectionTolerances {
39    fn default() -> Self {
40        Self {
41            collinearity_epsilon: Real::EPSILON * 100.0,
42        }
43    }
44}
45
46#[derive(Copy, Clone, Debug, PartialEq, Eq)]
47enum InFlag {
48    // The current neighborhood of the traversed point on poly1 is inside poly2.
49    Poly1IsInside,
50    // The current neighborhood of the traversed point on poly2 is inside poly1.
51    Poly2IsInside,
52    Unknown,
53}
54
55/// Location of a point on a polyline.
56///
57/// This enum represents where a point lies on a polygon's boundary. It's used by
58/// the intersection algorithms to precisely describe intersection points.
59///
60/// # Variants
61///
62/// * `OnVertex(i)` - The point is exactly on vertex `i` of the polygon
63/// * `OnEdge(i, j, bcoords)` - The point lies on the edge between vertices `i` and `j`,
64///   with barycentric coordinates `bcoords` where `bcoords[0] + bcoords[1] = 1.0`
65///
66/// # Examples
67///
68/// ```
69/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
70/// # use parry2d::transformation::PolylinePointLocation;
71/// # use parry2d::na::Point2;
72/// let polygon = vec![
73///     Point2::origin(),
74///     Point2::new(2.0, 0.0),
75///     Point2::new(2.0, 2.0),
76///     Point2::new(0.0, 2.0),
77/// ];
78///
79/// // A point on vertex 0
80/// let loc1 = PolylinePointLocation::OnVertex(0);
81/// let pt1 = loc1.to_point(&polygon);
82/// assert_eq!(pt1, Point2::origin());
83///
84/// // A point halfway along the edge from vertex 0 to vertex 1
85/// let loc2 = PolylinePointLocation::OnEdge(0, 1, [0.5, 0.5]);
86/// let pt2 = loc2.to_point(&polygon);
87/// assert_eq!(pt2, Point2::new(1.0, 0.0));
88/// # }
89/// ```
90#[derive(Copy, Clone, Debug, PartialEq)]
91pub enum PolylinePointLocation {
92    /// Point on a vertex.
93    OnVertex(usize),
94    /// Point on an edge.
95    OnEdge(usize, usize, [Real; 2]),
96}
97
98impl PolylinePointLocation {
99    /// The barycentric coordinates such that the point in the intersected segment `[a, b]` is
100    /// equal to `a + (b - a) * centered_bcoords`.
101    fn centered_bcoords(&self, edge: [usize; 2]) -> Real {
102        match self {
103            Self::OnVertex(vid) => {
104                if *vid == edge[0] {
105                    0.0
106                } else {
107                    1.0
108                }
109            }
110            Self::OnEdge(ia, ib, bcoords) => {
111                assert_eq!([*ia, *ib], edge);
112                bcoords[1]
113            }
114        }
115    }
116
117    /// Computes the point corresponding to this location.
118    ///
119    /// Given a polygon (as a slice of points), this method converts the location
120    /// into an actual 2D point coordinate.
121    ///
122    /// # Arguments
123    ///
124    /// * `pts` - The vertices of the polygon
125    ///
126    /// # Examples
127    ///
128    /// ```
129    /// # #[cfg(all(feature = "dim2", feature = "f32"))] {
130    /// # use parry2d::transformation::PolylinePointLocation;
131    /// # use parry2d::na::Point2;
132    /// let polygon = vec![
133    ///     Point2::origin(),
134    ///     Point2::new(4.0, 0.0),
135    ///     Point2::new(4.0, 4.0),
136    /// ];
137    ///
138    /// let loc = PolylinePointLocation::OnEdge(0, 1, [0.75, 0.25]);
139    /// let point = loc.to_point(&polygon);
140    /// assert_eq!(point, Point2::new(1.0, 0.0)); // 75% of vertex 0 + 25% of vertex 1
141    /// # }
142    /// ```
143    pub fn to_point(self, pts: &[Point2<Real>]) -> Point2<Real> {
144        match self {
145            PolylinePointLocation::OnVertex(i) => pts[i],
146            PolylinePointLocation::OnEdge(i1, i2, bcoords) => {
147                pts[i1] * bcoords[0] + pts[i2].coords * bcoords[1]
148            }
149        }
150    }
151
152    fn from_segment_point_location(a: usize, b: usize, loc: SegmentPointLocation) -> Self {
153        match loc {
154            SegmentPointLocation::OnVertex(0) => PolylinePointLocation::OnVertex(a),
155            SegmentPointLocation::OnVertex(1) => PolylinePointLocation::OnVertex(b),
156            SegmentPointLocation::OnVertex(_) => unreachable!(),
157            SegmentPointLocation::OnEdge(bcoords) => PolylinePointLocation::OnEdge(a, b, bcoords),
158        }
159    }
160}
161
162/// Computes the intersection points of two convex polygons.
163///
164/// This function takes two convex polygons and computes their intersection, returning
165/// the vertices of the resulting intersection polygon. The result is added to the `out` vector.
166///
167/// # Important Notes
168///
169/// - **Convex polygons only**: Both input polygons must be convex. For non-convex polygons,
170///   use [`polygons_intersection_points`] instead.
171/// - **Counter-clockwise winding**: Input polygons should be oriented counter-clockwise.
172/// - **Default tolerances**: Uses default numerical tolerances. For custom tolerances,
173///   use [`convex_polygons_intersection_points_with_tolerances`].
174///
175/// # Arguments
176///
177/// * `poly1` - First convex polygon as a slice of vertices
178/// * `poly2` - Second convex polygon as a slice of vertices
179/// * `out` - Output vector where intersection vertices will be appended
180///
181/// # Examples
182///
183/// ```
184/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
185/// # use parry2d::transformation::convex_polygons_intersection_points;
186/// # use parry2d::na::Point2;
187/// // Define two overlapping squares
188/// let square1 = vec![
189///     Point2::origin(),
190///     Point2::new(2.0, 0.0),
191///     Point2::new(2.0, 2.0),
192///     Point2::new(0.0, 2.0),
193/// ];
194///
195/// let square2 = vec![
196///     Point2::new(1.0, 1.0),
197///     Point2::new(3.0, 1.0),
198///     Point2::new(3.0, 3.0),
199///     Point2::new(1.0, 3.0),
200/// ];
201///
202/// let mut intersection = Vec::new();
203/// convex_polygons_intersection_points(&square1, &square2, &mut intersection);
204///
205/// // The intersection should be a square from (1,1) to (2,2)
206/// assert_eq!(intersection.len(), 4);
207/// # }
208/// ```
209///
210/// # See Also
211///
212/// * [`convex_polygons_intersection`] - For closure-based output
213/// * [`polygons_intersection_points`] - For non-convex polygons
214pub fn convex_polygons_intersection_points(
215    poly1: &[Point2<Real>],
216    poly2: &[Point2<Real>],
217    out: &mut Vec<Point2<Real>>,
218) {
219    convex_polygons_intersection_points_with_tolerances(poly1, poly2, Default::default(), out);
220}
221
222/// Computes the intersection points of two convex polygons with custom tolerances.
223///
224/// This is the same as [`convex_polygons_intersection_points`] but allows you to specify
225/// custom numerical tolerances for the intersection computation.
226///
227/// # Arguments
228///
229/// * `poly1` - First convex polygon as a slice of vertices
230/// * `poly2` - Second convex polygon as a slice of vertices
231/// * `tolerances` - Custom tolerances for numerical precision
232/// * `out` - Output vector where intersection vertices will be appended
233///
234/// # Examples
235///
236/// ```
237/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
238/// # use parry2d::transformation::{convex_polygons_intersection_points_with_tolerances, PolygonIntersectionTolerances};
239/// # use parry2d::na::Point2;
240/// let triangle1 = vec![
241///     Point2::origin(),
242///     Point2::new(4.0, 0.0),
243///     Point2::new(2.0, 3.0),
244/// ];
245///
246/// let triangle2 = vec![
247///     Point2::new(1.0, 0.5),
248///     Point2::new(3.0, 0.5),
249///     Point2::new(2.0, 2.5),
250/// ];
251///
252/// let mut intersection = Vec::new();
253/// let tolerances = PolygonIntersectionTolerances {
254///     collinearity_epsilon: 1.0e-6,
255/// };
256///
257/// convex_polygons_intersection_points_with_tolerances(
258///     &triangle1,
259///     &triangle2,
260///     tolerances,
261///     &mut intersection
262/// );
263///
264/// // The triangles overlap, so we should get intersection points
265/// assert!(intersection.len() >= 3);
266/// # }
267/// ```
268pub fn convex_polygons_intersection_points_with_tolerances(
269    poly1: &[Point2<Real>],
270    poly2: &[Point2<Real>],
271    tolerances: PolygonIntersectionTolerances,
272    out: &mut Vec<Point2<Real>>,
273) {
274    convex_polygons_intersection_with_tolerances(poly1, poly2, tolerances, |loc1, loc2| {
275        if let Some(loc1) = loc1 {
276            out.push(loc1.to_point(poly1))
277        } else if let Some(loc2) = loc2 {
278            out.push(loc2.to_point(poly2))
279        }
280    })
281}
282
283/// Computes the intersection of two convex polygons with closure-based output.
284///
285/// This function is similar to [`convex_polygons_intersection_points`] but provides more
286/// flexibility by calling a closure for each intersection vertex. The closure receives
287/// the location of the vertex on each polygon (if applicable).
288///
289/// This is useful when you need to track which polygon each intersection vertex comes from,
290/// or when you want to process vertices as they're computed rather than collecting them all.
291///
292/// # Arguments
293///
294/// * `poly1` - First convex polygon as a slice of vertices
295/// * `poly2` - Second convex polygon as a slice of vertices
296/// * `out` - Closure called for each intersection vertex with its location on both polygons
297///
298/// # Closure Arguments
299///
300/// The closure receives `(Option<PolylinePointLocation>, Option<PolylinePointLocation>)`:
301/// - If the point comes from `poly1`, the first option contains its location on `poly1`
302/// - If the point comes from `poly2`, the second option contains its location on `poly2`
303/// - At least one of the options will always be `Some`
304///
305/// # Examples
306///
307/// ```
308/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
309/// # use parry2d::transformation::convex_polygons_intersection;
310/// # use parry2d::na::Point2;
311/// let square = vec![
312///     Point2::origin(),
313///     Point2::new(2.0, 0.0),
314///     Point2::new(2.0, 2.0),
315///     Point2::new(0.0, 2.0),
316/// ];
317///
318/// let diamond = vec![
319///     Point2::new(1.0, -0.5),
320///     Point2::new(2.5, 1.0),
321///     Point2::new(1.0, 2.5),
322///     Point2::new(-0.5, 1.0),
323/// ];
324///
325/// let mut intersection_points = Vec::new();
326/// convex_polygons_intersection(&square, &diamond, |loc1, loc2| {
327///     if let Some(loc) = loc1 {
328///         intersection_points.push(loc.to_point(&square));
329///     } else if let Some(loc) = loc2 {
330///         intersection_points.push(loc.to_point(&diamond));
331///     }
332/// });
333///
334/// // The intersection should have multiple vertices
335/// assert!(intersection_points.len() >= 3);
336/// # }
337/// ```
338///
339/// # See Also
340///
341/// * [`convex_polygons_intersection_points`] - Simpler vector-based output
342/// * [`convex_polygons_intersection_with_tolerances`] - With custom tolerances
343pub fn convex_polygons_intersection(
344    poly1: &[Point2<Real>],
345    poly2: &[Point2<Real>],
346    out: impl FnMut(Option<PolylinePointLocation>, Option<PolylinePointLocation>),
347) {
348    convex_polygons_intersection_with_tolerances(poly1, poly2, Default::default(), out)
349}
350
351/// Computes the intersection of two convex polygons with custom tolerances and closure-based output.
352///
353/// This is the most flexible version of the convex polygon intersection function, combining
354/// custom tolerances with closure-based output for maximum control.
355///
356/// # Arguments
357///
358/// * `poly1` - First convex polygon as a slice of vertices
359/// * `poly2` - Second convex polygon as a slice of vertices
360/// * `tolerances` - Custom tolerances for numerical precision
361/// * `out` - Closure called for each intersection vertex
362///
363/// # Algorithm
364///
365/// This function implements the Sutherland-Hodgman-like algorithm for convex polygon
366/// intersection. It works by:
367/// 1. Traversing the edges of both polygons simultaneously
368/// 2. Detecting edge-edge intersections
369/// 3. Determining which vertices are inside the other polygon
370/// 4. Outputting the vertices of the intersection polygon in order
371///
372/// # Examples
373///
374/// ```
375/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
376/// # use parry2d::transformation::{convex_polygons_intersection_with_tolerances, PolygonIntersectionTolerances};
377/// # use parry2d::na::Point2;
378/// let hexagon = vec![
379///     Point2::new(2.0, 0.0),
380///     Point2::new(1.0, 1.732),
381///     Point2::new(-1.0, 1.732),
382///     Point2::new(-2.0, 0.0),
383///     Point2::new(-1.0, -1.732),
384///     Point2::new(1.0, -1.732),
385/// ];
386///
387/// let square = vec![
388///     Point2::new(-1.0, -1.0),
389///     Point2::new(1.0, -1.0),
390///     Point2::new(1.0, 1.0),
391///     Point2::new(-1.0, 1.0),
392/// ];
393///
394/// let tolerances = PolygonIntersectionTolerances::default();
395/// let mut intersection_points = Vec::new();
396///
397/// convex_polygons_intersection_with_tolerances(
398///     &hexagon,
399///     &square,
400///     tolerances,
401///     |loc1, loc2| {
402///         if let Some(loc) = loc1 {
403///             intersection_points.push(loc.to_point(&hexagon));
404///         } else if let Some(loc) = loc2 {
405///             intersection_points.push(loc.to_point(&square));
406///         }
407///     }
408/// );
409///
410/// // The intersection should form a polygon
411/// assert!(intersection_points.len() >= 3);
412/// # }
413/// ```
414pub fn convex_polygons_intersection_with_tolerances(
415    poly1: &[Point2<Real>],
416    poly2: &[Point2<Real>],
417    tolerances: PolygonIntersectionTolerances,
418    mut out: impl FnMut(Option<PolylinePointLocation>, Option<PolylinePointLocation>),
419) {
420    // TODO: this does not handle correctly the case where the
421    // first triangle of the polygon is degenerate.
422    let rev1 = poly1.len() > 2
423        && Triangle::orientation2d(
424            &poly1[0],
425            &poly1[1],
426            &poly1[2],
427            tolerances.collinearity_epsilon,
428        ) == TriangleOrientation::Clockwise;
429    let rev2 = poly2.len() > 2
430        && Triangle::orientation2d(
431            &poly2[0],
432            &poly2[1],
433            &poly2[2],
434            tolerances.collinearity_epsilon,
435        ) == TriangleOrientation::Clockwise;
436
437    let len1 = poly1.len();
438    let len2 = poly2.len();
439
440    let mut i1 = 0; // Current index on the first polyline.
441    let mut i2 = 0; // Current index on the second polyline.
442    let mut nsteps1 = 0; // Number of times we advanced on the first polyline.
443    let mut nsteps2 = 0; // Number of times we advanced on the second polyline.
444    let mut inflag = InFlag::Unknown;
445    let mut first_point_found = false;
446
447    // Quit when both adv. indices have cycled, or one has cycled twice.
448    while (nsteps1 < len1 || nsteps2 < len2) && nsteps1 < 2 * len1 && nsteps2 < 2 * len2 {
449        let (a1, b1) = if rev1 {
450            ((len1 - i1) % len1, len1 - i1 - 1)
451        } else {
452            // Point before `i1`, and point at `i1`.
453            ((i1 + len1 - 1) % len1, i1)
454        };
455
456        let (a2, b2) = if rev2 {
457            ((len2 - i2) % len2, len2 - i2 - 1)
458        } else {
459            // Point before `i2`, and point at `i2`.
460            ((i2 + len2 - 1) % len2, i2)
461        };
462
463        let dir_edge1 = poly1[b1] - poly1[a1];
464        let dir_edge2 = poly2[b2] - poly2[a2];
465
466        // If there is an intersection, this will determine if the edge from poly2 is transitioning
467        // Left -> Right (CounterClockwise) or Right -> Left (Clockwise) relative to the edge from
468        // poly1.
469        let cross = Triangle::orientation2d(
470            &Point2::origin(),
471            &Point2::from(dir_edge1),
472            &Point2::from(dir_edge2),
473            tolerances.collinearity_epsilon,
474        );
475        // Determines if b1 is left (CounterClockwise) or right (Clockwise) of [a2, b2].
476        let a2_b2_b1 = Triangle::orientation2d(
477            &poly2[a2],
478            &poly2[b2],
479            &poly1[b1],
480            tolerances.collinearity_epsilon,
481        );
482        // Determines if b2 is left (CounterClockwise) or right (Clockwise) of [a1, b1].
483        let a1_b1_b2 = Triangle::orientation2d(
484            &poly1[a1],
485            &poly1[b1],
486            &poly2[b2],
487            tolerances.collinearity_epsilon,
488        );
489
490        // If edge1 & edge2 intersect, update inflag.
491        if let Some(inter) = utils::segments_intersection2d(
492            &poly1[a1],
493            &poly1[b1],
494            &poly2[a2],
495            &poly2[b2],
496            tolerances.collinearity_epsilon,
497        ) {
498            match inter {
499                SegmentsIntersection::Point { loc1, loc2 } => {
500                    if a2_b2_b1 != TriangleOrientation::Degenerate
501                        && a1_b1_b2 != TriangleOrientation::Degenerate
502                    {
503                        let loc1 = PolylinePointLocation::from_segment_point_location(a1, b1, loc1);
504                        let loc2 = PolylinePointLocation::from_segment_point_location(a2, b2, loc2);
505                        out(Some(loc1), Some(loc2));
506
507                        if inflag == InFlag::Unknown && !first_point_found {
508                            // This is the first point, reset the number of steps since we are
509                            // effectively starting the actual traversal now.
510                            nsteps1 = 0;
511                            nsteps2 = 0;
512                            first_point_found = true;
513                        }
514
515                        if a2_b2_b1 == TriangleOrientation::CounterClockwise {
516                            // The point b1 is left of [a2, b2] so it is inside poly2 ???
517                            inflag = InFlag::Poly1IsInside;
518                        } else if a1_b1_b2 == TriangleOrientation::CounterClockwise {
519                            // The point b2 is left of [a1, b1] so it is inside poly1 ???
520                            inflag = InFlag::Poly2IsInside;
521                        }
522                    }
523                }
524                SegmentsIntersection::Segment {
525                    first_loc1,
526                    first_loc2,
527                    second_loc1,
528                    second_loc2,
529                } => {
530                    if dir_edge1.dot(&dir_edge2) < 0.0 {
531                        // Special case: edge1 & edge2 overlap and oppositely oriented. The
532                        //               intersection is degenerate (equals to a segment). Output
533                        //               the segment and exit.
534                        let loc1 =
535                            PolylinePointLocation::from_segment_point_location(a1, b1, first_loc1);
536                        let loc2 =
537                            PolylinePointLocation::from_segment_point_location(a2, b2, first_loc2);
538                        out(Some(loc1), Some(loc2));
539
540                        let loc1 =
541                            PolylinePointLocation::from_segment_point_location(a1, b1, second_loc1);
542                        let loc2 =
543                            PolylinePointLocation::from_segment_point_location(a2, b2, second_loc2);
544                        out(Some(loc1), Some(loc2));
545                        return;
546                    }
547                }
548            }
549        }
550
551        // Special case: edge1 & edge2 parallel and separated.
552        if cross == TriangleOrientation::Degenerate
553            && a2_b2_b1 == TriangleOrientation::Clockwise
554            && a1_b1_b2 == TriangleOrientation::Clockwise
555        // TODO: should this also include any case where a2_b2_b1 and a1_b1_b2 are both different from Degenerate?
556        {
557            return;
558        }
559        // Special case: edge1 & edge2 collinear.
560        else if cross == TriangleOrientation::Degenerate
561            && a2_b2_b1 == TriangleOrientation::Degenerate
562            && a1_b1_b2 == TriangleOrientation::Degenerate
563        {
564            // Advance but do not output point.
565            if inflag == InFlag::Poly1IsInside {
566                i2 = advance(i2, &mut nsteps2, len2);
567            } else {
568                i1 = advance(i1, &mut nsteps1, len1);
569            }
570        }
571        // Generic cases.
572        else if cross == TriangleOrientation::CounterClockwise {
573            if a1_b1_b2 == TriangleOrientation::CounterClockwise {
574                if inflag == InFlag::Poly1IsInside {
575                    out(Some(PolylinePointLocation::OnVertex(b1)), None)
576                }
577                i1 = advance(i1, &mut nsteps1, len1);
578            } else {
579                if inflag == InFlag::Poly2IsInside {
580                    out(None, Some(PolylinePointLocation::OnVertex(b2)))
581                }
582                i2 = advance(i2, &mut nsteps2, len2);
583            }
584        } else {
585            // We have cross == TriangleOrientation::Clockwise.
586            if a2_b2_b1 == TriangleOrientation::CounterClockwise {
587                if inflag == InFlag::Poly2IsInside {
588                    out(None, Some(PolylinePointLocation::OnVertex(b2)))
589                }
590                i2 = advance(i2, &mut nsteps2, len2);
591            } else {
592                if inflag == InFlag::Poly1IsInside {
593                    out(Some(PolylinePointLocation::OnVertex(b1)), None)
594                }
595                i1 = advance(i1, &mut nsteps1, len1);
596            }
597        }
598    }
599
600    if !first_point_found {
601        // No intersection: test if one polygon completely encloses the other.
602        let mut orient = TriangleOrientation::Degenerate;
603        let mut ok = true;
604
605        // TODO: avoid the O(n²) complexity.
606        for a in 0..len1 {
607            for p2 in poly2 {
608                let a_minus_1 = (a + len1 - 1) % len1; // a - 1
609                let new_orient = Triangle::orientation2d(
610                    &poly1[a_minus_1],
611                    &poly1[a],
612                    p2,
613                    tolerances.collinearity_epsilon,
614                );
615
616                if orient == TriangleOrientation::Degenerate {
617                    orient = new_orient
618                } else if new_orient != orient && new_orient != TriangleOrientation::Degenerate {
619                    ok = false;
620                    break;
621                }
622            }
623        }
624
625        if ok {
626            for mut b in 0..len2 {
627                if rev2 {
628                    b = len2 - b - 1;
629                }
630                out(None, Some(PolylinePointLocation::OnVertex(b)))
631            }
632        }
633
634        let mut orient = TriangleOrientation::Degenerate;
635        let mut ok = true;
636
637        // TODO: avoid the O(n²) complexity.
638        for b in 0..len2 {
639            for p1 in poly1 {
640                let b_minus_1 = (b + len2 - 1) % len2; // = b - 1
641                let new_orient = Triangle::orientation2d(
642                    &poly2[b_minus_1],
643                    &poly2[b],
644                    p1,
645                    tolerances.collinearity_epsilon,
646                );
647
648                if orient == TriangleOrientation::Degenerate {
649                    orient = new_orient
650                } else if new_orient != orient && new_orient != TriangleOrientation::Degenerate {
651                    ok = false;
652                    break;
653                }
654            }
655        }
656
657        if ok {
658            for mut a in 0..len1 {
659                if rev1 {
660                    a = len1 - a - 1;
661                }
662                out(Some(PolylinePointLocation::OnVertex(a)), None)
663            }
664        }
665    }
666}
667
668#[inline]
669fn advance(a: usize, aa: &mut usize, n: usize) -> usize {
670    *aa += 1;
671    (a + 1) % n
672}
673
674/// Error type for polygon intersection operations.
675///
676/// This error can occur when computing intersections of non-convex polygons.
677#[derive(thiserror::Error, Debug)]
678pub enum PolygonsIntersectionError {
679    /// An infinite loop was detected during intersection computation.
680    ///
681    /// This typically indicates that the input polygons are ill-formed, such as:
682    /// - Self-intersecting polygons
683    /// - Polygons with duplicate or degenerate edges
684    /// - Polygons with inconsistent winding order
685    #[error("Infinite loop detected; input polygons are ill-formed.")]
686    InfiniteLoop,
687}
688
689/// Computes the intersection points of two possibly non-convex polygons.
690///
691/// This function handles both convex and **non-convex (concave)** polygons, making it more
692/// general than [`convex_polygons_intersection_points`]. However, it requires that:
693/// - Neither polygon self-intersects
694/// - Both polygons are oriented counter-clockwise
695///
696/// The result is a vector of polygons, where each polygon represents one connected component
697/// of the intersection. In most cases there will be only one component, but complex intersections
698/// can produce multiple separate regions.
699///
700/// # Important Notes
701///
702/// - **Non-convex support**: This function works with concave polygons
703/// - **Multiple components**: Returns a `Vec<Vec<Point2<Real>>>` because the intersection
704///   of two concave polygons can produce multiple separate regions
705/// - **No self-intersection**: Input polygons must not self-intersect
706/// - **Counter-clockwise winding**: Both polygons must be oriented counter-clockwise
707/// - **Performance**: Slower than [`convex_polygons_intersection_points`]. If both polygons
708///   are convex, use that function instead.
709///
710/// # Arguments
711///
712/// * `poly1` - First polygon as a slice of vertices
713/// * `poly2` - Second polygon as a slice of vertices
714///
715/// # Returns
716///
717/// * `Ok(Vec<Vec<Point2<Real>>>)` - A vector of intersection polygons (usually just one)
718/// * `Err(PolygonsIntersectionError::InfiniteLoop)` - If the polygons are ill-formed
719///
720/// # Examples
721///
722/// ## Example 1: Two non-convex polygons
723///
724/// ```
725/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
726/// # use parry2d::transformation::polygons_intersection_points;
727/// # use parry2d::na::Point2;
728/// // L-shaped polygon
729/// let l_shape = vec![
730///     Point2::origin(),
731///     Point2::new(3.0, 0.0),
732///     Point2::new(3.0, 1.0),
733///     Point2::new(1.0, 1.0),
734///     Point2::new(1.0, 3.0),
735///     Point2::new(0.0, 3.0),
736/// ];
737///
738/// // Square overlapping the L-shape
739/// let square = vec![
740///     Point2::new(0.5, 0.5),
741///     Point2::new(2.5, 0.5),
742///     Point2::new(2.5, 2.5),
743///     Point2::new(0.5, 2.5),
744/// ];
745///
746/// let result = polygons_intersection_points(&l_shape, &square).unwrap();
747///
748/// // Should have intersection regions
749/// assert!(!result.is_empty());
750/// # }
751/// ```
752///
753/// ## Example 2: Convex polygons (also works)
754///
755/// ```
756/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
757/// # use parry2d::transformation::polygons_intersection_points;
758/// # use parry2d::na::Point2;
759/// let triangle = vec![
760///     Point2::origin(),
761///     Point2::new(4.0, 0.0),
762///     Point2::new(2.0, 3.0),
763/// ];
764///
765/// let square = vec![
766///     Point2::new(1.0, 0.5),
767///     Point2::new(3.0, 0.5),
768///     Point2::new(3.0, 2.0),
769///     Point2::new(1.0, 2.0),
770/// ];
771///
772/// let result = polygons_intersection_points(&triangle, &square).unwrap();
773/// assert_eq!(result.len(), 1); // One intersection region
774/// # }
775/// ```
776///
777/// # Errors
778///
779/// Returns `PolygonsIntersectionError::InfiniteLoop` if:
780/// - Either polygon self-intersects
781/// - The polygons have degenerate or duplicate edges
782/// - The winding order is inconsistent
783///
784/// # See Also
785///
786/// * [`convex_polygons_intersection_points`] - Faster version for convex polygons only
787/// * [`polygons_intersection`] - Closure-based version with more control
788pub fn polygons_intersection_points(
789    poly1: &[Point2<Real>],
790    poly2: &[Point2<Real>],
791) -> Result<Vec<Vec<Point2<Real>>>, PolygonsIntersectionError> {
792    let mut result = vec![];
793    let mut curr_poly = vec![];
794    polygons_intersection(poly1, poly2, |loc1, loc2| {
795        if let Some(loc1) = loc1 {
796            curr_poly.push(loc1.to_point(poly1))
797        } else if let Some(loc2) = loc2 {
798            curr_poly.push(loc2.to_point(poly2))
799        } else if !curr_poly.is_empty() {
800            result.push(core::mem::take(&mut curr_poly));
801        }
802    })?;
803
804    Ok(result)
805}
806
807/// Computes the intersection of two possibly non-convex polygons with closure-based output.
808///
809/// This is the closure-based version of [`polygons_intersection_points`], providing more
810/// control over how intersection vertices are processed. It handles non-convex (concave)
811/// polygons but requires they do not self-intersect.
812///
813/// # Arguments
814///
815/// * `poly1` - First polygon as a slice of vertices
816/// * `poly2` - Second polygon as a slice of vertices
817/// * `out` - Closure called for each intersection vertex or component separator
818///
819/// # Closure Arguments
820///
821/// The closure receives `(Option<PolylinePointLocation>, Option<PolylinePointLocation>)`:
822/// - When **both are `Some`**: An edge-edge intersection point
823/// - When **one is `Some`**: A vertex from one polygon inside the other
824/// - When **both are `None`**: Marks the end of a connected component
825///
826/// # Algorithm
827///
828/// This function uses a graph-based traversal algorithm:
829/// 1. Finds all edge-edge intersection points between the two polygons
830/// 2. Builds a graph where vertices are intersection points
831/// 3. Traverses the graph, alternating between polygons at intersection points
832/// 4. Outputs vertices that form the intersection boundary
833///
834/// # Examples
835///
836/// ## Example 1: Collecting intersection points
837///
838/// ```
839/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
840/// # use parry2d::transformation::polygons_intersection;
841/// # use parry2d::na::Point2;
842/// let pentagon = vec![
843///     Point2::new(1.0, 0.0),
844///     Point2::new(0.309, 0.951),
845///     Point2::new(-0.809, 0.588),
846///     Point2::new(-0.809, -0.588),
847///     Point2::new(0.309, -0.951),
848/// ];
849///
850/// let square = vec![
851///     Point2::new(-0.5, -0.5),
852///     Point2::new(0.5, -0.5),
853///     Point2::new(0.5, 0.5),
854///     Point2::new(-0.5, 0.5),
855/// ];
856///
857/// let mut components = Vec::new();
858/// let mut current_component = Vec::new();
859///
860/// polygons_intersection(&pentagon, &square, |loc1, loc2| {
861///     if loc1.is_none() && loc2.is_none() {
862///         // End of a component
863///         if !current_component.is_empty() {
864///             components.push(std::mem::take(&mut current_component));
865///         }
866///     } else if let Some(loc) = loc1 {
867///         current_component.push(loc.to_point(&pentagon));
868///     } else if let Some(loc) = loc2 {
869///         current_component.push(loc.to_point(&square));
870///     }
871/// }).unwrap();
872///
873/// // Should have at least one intersection component
874/// assert!(!components.is_empty());
875/// # }
876/// ```
877///
878/// ## Example 2: Counting intersection vertices
879///
880/// ```
881/// # #[cfg(all(feature = "dim2", feature = "f32"))] {
882/// # use parry2d::transformation::polygons_intersection;
883/// # use parry2d::na::Point2;
884/// let hexagon = vec![
885///     Point2::new(2.0, 0.0),
886///     Point2::new(1.0, 1.732),
887///     Point2::new(-1.0, 1.732),
888///     Point2::new(-2.0, 0.0),
889///     Point2::new(-1.0, -1.732),
890///     Point2::new(1.0, -1.732),
891/// ];
892///
893/// let circle_approx = vec![
894///     Point2::new(1.0, 0.0),
895///     Point2::new(0.707, 0.707),
896///     Point2::new(0.0, 1.0),
897///     Point2::new(-0.707, 0.707),
898///     Point2::new(-1.0, 0.0),
899///     Point2::new(-0.707, -0.707),
900///     Point2::new(0.0, -1.0),
901///     Point2::new(0.707, -0.707),
902/// ];
903///
904/// let mut vertex_count = 0;
905/// polygons_intersection(&hexagon, &circle_approx, |loc1, loc2| {
906///     if loc1.is_some() || loc2.is_some() {
907///         vertex_count += 1;
908///     }
909/// }).unwrap();
910///
911/// assert!(vertex_count > 0);
912/// # }
913/// ```
914///
915/// # Errors
916///
917/// Returns `PolygonsIntersectionError::InfiniteLoop` if the polygons are ill-formed.
918///
919/// # See Also
920///
921/// * [`polygons_intersection_points`] - Simpler vector-based output
922/// * [`convex_polygons_intersection`] - Faster version for convex polygons
923pub fn polygons_intersection(
924    poly1: &[Point2<Real>],
925    poly2: &[Point2<Real>],
926    mut out: impl FnMut(Option<PolylinePointLocation>, Option<PolylinePointLocation>),
927) -> Result<(), PolygonsIntersectionError> {
928    let tolerances = PolygonIntersectionTolerances::default();
929
930    #[derive(Debug)]
931    struct ToTraverse {
932        poly: usize,
933        edge: EdgeId,
934    }
935
936    let (intersections, num_intersections) =
937        compute_sorted_edge_intersections(poly1, poly2, tolerances.collinearity_epsilon);
938    let mut visited = vec![false; num_intersections];
939    let segment = |eid: EdgeId, poly: &[Point2<Real>]| [poly[eid], poly[(eid + 1) % poly.len()]];
940
941    // Traverse all the intersections.
942    for inters in intersections[0].values() {
943        for inter in inters {
944            if visited[inter.id] {
945                continue;
946            }
947
948            // We found an intersection we haven’t visited yet, traverse the loop, alternating
949            // between poly1 and poly2 when reaching an intersection.
950            let [a1, b1] = segment(inter.edges[0], poly1);
951            let [a2, b2] = segment(inter.edges[1], poly2);
952            let poly_to_traverse =
953                match Triangle::orientation2d(&a1, &b1, &a2, tolerances.collinearity_epsilon) {
954                    TriangleOrientation::Clockwise => 1,
955                    TriangleOrientation::CounterClockwise => 0,
956                    TriangleOrientation::Degenerate => {
957                        match Triangle::orientation2d(
958                            &a1,
959                            &b1,
960                            &b2,
961                            tolerances.collinearity_epsilon,
962                        ) {
963                            TriangleOrientation::Clockwise => 0,
964                            TriangleOrientation::CounterClockwise => 1,
965                            TriangleOrientation::Degenerate => {
966                                log::debug!("Unhandled edge-edge overlap case.");
967                                0
968                            }
969                        }
970                    }
971                };
972
973            #[derive(Debug)]
974            enum TraversalStatus {
975                OnVertex,
976                OnIntersection(usize),
977            }
978
979            let polys = [poly1, poly2];
980            let mut to_traverse = ToTraverse {
981                poly: poly_to_traverse,
982                edge: inter.edges[poly_to_traverse],
983            };
984
985            let mut status = TraversalStatus::OnIntersection(inter.id);
986
987            for loop_id in 0.. {
988                if loop_id > poly1.len() * poly2.len() {
989                    return Err(PolygonsIntersectionError::InfiniteLoop);
990                }
991
992                let empty = Vec::new();
993                let edge_inters = intersections[to_traverse.poly]
994                    .get(&to_traverse.edge)
995                    .unwrap_or(&empty);
996
997                match status {
998                    TraversalStatus::OnIntersection(inter_id) => {
999                        let (curr_inter_pos, curr_inter) = edge_inters
1000                            .iter()
1001                            .enumerate()
1002                            .find(|(_, inter)| inter.id == inter_id)
1003                            .unwrap_or_else(|| unreachable!());
1004
1005                        if visited[curr_inter.id] {
1006                            // We already saw this intersection: we looped back to the start of
1007                            // the intersection polygon.
1008                            out(None, None);
1009                            break;
1010                        }
1011
1012                        out(Some(curr_inter.locs[0]), Some(curr_inter.locs[1]));
1013                        visited[curr_inter.id] = true;
1014
1015                        if curr_inter_pos + 1 < edge_inters.len() {
1016                            // There are other intersections after this one.
1017                            // Move forward to the next intersection point and move on to traversing
1018                            // the other polygon.
1019                            let next_inter = &edge_inters[curr_inter_pos + 1];
1020                            to_traverse.poly = (to_traverse.poly + 1) % 2;
1021                            to_traverse.edge = next_inter.edges[to_traverse.poly];
1022                            status = TraversalStatus::OnIntersection(next_inter.id);
1023                        } else {
1024                            // This was the last intersection, move to the next vertex on the
1025                            // same polygon.
1026                            to_traverse.edge =
1027                                (to_traverse.edge + 1) % polys[to_traverse.poly].len();
1028                            status = TraversalStatus::OnVertex;
1029                        }
1030                    }
1031                    TraversalStatus::OnVertex => {
1032                        let location = PolylinePointLocation::OnVertex(to_traverse.edge);
1033
1034                        if to_traverse.poly == 0 {
1035                            out(Some(location), None);
1036                        } else {
1037                            out(None, Some(location))
1038                        };
1039
1040                        if let Some(first_intersection) = edge_inters.first() {
1041                            // Jump on the first intersection and move on to the other polygon.
1042                            to_traverse.poly = (to_traverse.poly + 1) % 2;
1043                            to_traverse.edge = first_intersection.edges[to_traverse.poly];
1044                            status = TraversalStatus::OnIntersection(first_intersection.id);
1045                        } else {
1046                            // Move forward to the next vertex/edge on the same polygon.
1047                            to_traverse.edge =
1048                                (to_traverse.edge + 1) % polys[to_traverse.poly].len();
1049                        }
1050                    }
1051                }
1052            }
1053        }
1054    }
1055
1056    // If there are no intersection, check if one polygon is inside the other.
1057    if intersections[0].is_empty() {
1058        if utils::point_in_poly2d(&poly1[0], poly2) {
1059            for pt_id in 0..poly1.len() {
1060                out(Some(PolylinePointLocation::OnVertex(pt_id)), None)
1061            }
1062            out(None, None);
1063        } else if utils::point_in_poly2d(&poly2[0], poly1) {
1064            for pt_id in 0..poly2.len() {
1065                out(None, Some(PolylinePointLocation::OnVertex(pt_id)))
1066            }
1067            out(None, None);
1068        }
1069    }
1070
1071    Ok(())
1072}
1073
1074type EdgeId = usize;
1075type IntersectionId = usize;
1076
1077#[derive(Copy, Clone, Debug)]
1078struct IntersectionPoint {
1079    id: IntersectionId,
1080    edges: [EdgeId; 2],
1081    locs: [PolylinePointLocation; 2],
1082}
1083
1084fn compute_sorted_edge_intersections(
1085    poly1: &[Point2<Real>],
1086    poly2: &[Point2<Real>],
1087    eps: Real,
1088) -> ([HashMap<EdgeId, Vec<IntersectionPoint>>; 2], usize) {
1089    let mut inter1: HashMap<EdgeId, Vec<IntersectionPoint>> = HashMap::default();
1090    let mut inter2: HashMap<EdgeId, Vec<IntersectionPoint>> = HashMap::default();
1091    let mut id = 0;
1092
1093    // Find the intersections.
1094    // TODO: this is a naive O(n²) check. Could use an acceleration structure for large polygons.
1095    for i1 in 0..poly1.len() {
1096        let j1 = (i1 + 1) % poly1.len();
1097
1098        for i2 in 0..poly2.len() {
1099            let j2 = (i2 + 1) % poly2.len();
1100
1101            let Some(inter) =
1102                utils::segments_intersection2d(&poly1[i1], &poly1[j1], &poly2[i2], &poly2[j2], eps)
1103            else {
1104                continue;
1105            };
1106
1107            match inter {
1108                SegmentsIntersection::Point { loc1, loc2 } => {
1109                    let loc1 = PolylinePointLocation::from_segment_point_location(i1, j1, loc1);
1110                    let loc2 = PolylinePointLocation::from_segment_point_location(i2, j2, loc2);
1111                    let intersection = IntersectionPoint {
1112                        id,
1113                        edges: [i1, i2],
1114                        locs: [loc1, loc2],
1115                    };
1116                    inter1.entry(i1).or_default().push(intersection);
1117                    inter2.entry(i2).or_default().push(intersection);
1118                    id += 1;
1119                }
1120                SegmentsIntersection::Segment { .. } => {
1121                    // TODO
1122                    log::debug!(
1123                        "Collinear segment-segment intersections not properly handled yet."
1124                    );
1125                }
1126            }
1127        }
1128    }
1129
1130    // Sort the intersections.
1131    for inters in inter1.values_mut() {
1132        inters.sort_by_key(|a| {
1133            let edge = [a.edges[0], (a.edges[0] + 1) % poly1.len()];
1134            OrderedFloat(a.locs[0].centered_bcoords(edge))
1135        });
1136    }
1137
1138    for inters in inter2.values_mut() {
1139        inters.sort_by_key(|a| {
1140            let edge = [a.edges[1], (a.edges[1] + 1) % poly2.len()];
1141            OrderedFloat(a.locs[1].centered_bcoords(edge))
1142        });
1143    }
1144
1145    ([inter1, inter2], id)
1146}
1147
1148#[cfg(all(test, feature = "dim2"))]
1149mod test {
1150    use crate::query::PointQuery;
1151    use crate::shape::Triangle;
1152    use crate::transformation::convex_polygons_intersection_points_with_tolerances;
1153    use crate::transformation::polygon_intersection::PolygonIntersectionTolerances;
1154    use alloc::vec::Vec;
1155    use na::Point2;
1156    use std::println;
1157
1158    #[test]
1159    fn intersect_triangle_common_vertex() {
1160        let tris = [
1161            (
1162                Triangle::new(
1163                    Point2::new(-0.0008759537858568062, -2.0103871966663305),
1164                    Point2::new(0.3903908709629763, -1.3421764825890266),
1165                    Point2::new(1.3380817875388151, -2.0098007857739013),
1166                ),
1167                Triangle::new(
1168                    Point2::new(0.0, -0.0),
1169                    Point2::new(-0.0008759537858568062, -2.0103871966663305),
1170                    Point2::new(1.9991979155226394, -2.009511242880474),
1171                ),
1172            ),
1173            (
1174                Triangle::new(
1175                    Point2::new(0.7319315811016305, -0.00004046981523721891),
1176                    Point2::new(2.0004914907008944, -0.00011061077714557787),
1177                    Point2::new(1.1848406021956144, -0.8155712451545468),
1178                ),
1179                Triangle::new(
1180                    Point2::origin(),
1181                    Point2::new(0.00011061077714557787, -2.000024893134292),
1182                    Point2::new(2.0004914907008944, -0.00011061077714557787),
1183                ),
1184            ),
1185            (
1186                Triangle::new(
1187                    Point2::new(-0.000049995168258705205, -0.9898801451981707),
1188                    Point2::new(0.0, -0.0),
1189                    Point2::new(0.583013294019752, -1.4170136900568633),
1190                ),
1191                Triangle::new(
1192                    Point2::new(0.0, -0.0),
1193                    Point2::new(-0.00010101395240669591, -2.000027389553894),
1194                    Point2::new(2.000372544168497, 0.00010101395240669591),
1195                ),
1196            ),
1197            (
1198                Triangle::new(
1199                    Point2::new(-0.940565646581769, -0.939804943675256),
1200                    Point2::new(0.0, -0.0),
1201                    Point2::new(-0.001533592366792066, -0.9283586484736431),
1202                ),
1203                Triangle::new(
1204                    Point2::new(0.0, -0.0),
1205                    Point2::new(-2.00752629829582, -2.0059026672784825),
1206                    Point2::new(-0.0033081650580626698, -2.0025945022204197),
1207                ),
1208            ),
1209        ];
1210
1211        for (tri1, tri2) in tris {
1212            let mut inter = Vec::new();
1213            let tolerances = PolygonIntersectionTolerances {
1214                collinearity_epsilon: 1.0e-5,
1215            };
1216            convex_polygons_intersection_points_with_tolerances(
1217                tri1.vertices(),
1218                tri2.vertices(),
1219                tolerances,
1220                &mut inter,
1221            );
1222
1223            println!("----------");
1224            println!("inter: {:?}", inter);
1225            println!(
1226                "tri1 is in tri2: {}",
1227                tri1.vertices().iter().all(|pt| tri2
1228                    .project_local_point(pt, false)
1229                    .is_inside_eps(pt, 1.0e-5))
1230            );
1231            println!(
1232                "tri2 is in tri1: {}",
1233                tri2.vertices().iter().all(|pt| tri1
1234                    .project_local_point(pt, false)
1235                    .is_inside_eps(pt, 1.0e-5))
1236            );
1237            for pt in &inter {
1238                let proj1 = tri1.project_local_point(&pt, false);
1239                let proj2 = tri2.project_local_point(&pt, false);
1240                assert!(proj1.is_inside_eps(&pt, 1.0e-5));
1241                assert!(proj2.is_inside_eps(&pt, 1.0e-5));
1242            }
1243        }
1244    }
1245}