parry3d/transformation/
polygon_intersection.rs

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