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#[derive(Copy, Clone, PartialEq, Debug)]
12pub struct PolygonIntersectionTolerances {
13    /// The epsilon given to [`Triangle::orientation2d`] for detecting if three points are collinear.
14    ///
15    /// Three points forming a triangle with an area smaller than this value are considered collinear.
16    pub collinearity_epsilon: Real,
17}
18
19impl Default for PolygonIntersectionTolerances {
20    fn default() -> Self {
21        Self {
22            collinearity_epsilon: Real::EPSILON * 100.0,
23        }
24    }
25}
26
27#[derive(Copy, Clone, Debug, PartialEq, Eq)]
28enum InFlag {
29    // The current neighborhood of the traversed point on poly1 is inside poly2.
30    Poly1IsInside,
31    // The current neighborhood of the traversed point on poly2 is inside poly1.
32    Poly2IsInside,
33    Unknown,
34}
35
36/// Location of a point on a polyline.
37#[derive(Copy, Clone, Debug, PartialEq)]
38pub enum PolylinePointLocation {
39    /// Point on a vertex.
40    OnVertex(usize),
41    /// Point on an edge.
42    OnEdge(usize, usize, [Real; 2]),
43}
44
45impl PolylinePointLocation {
46    /// The barycentric coordinates such that the point in the intersected segment `[a, b]` is
47    /// equal to `a + (b - a) * centered_bcoords`.
48    fn centered_bcoords(&self, edge: [usize; 2]) -> Real {
49        match self {
50            Self::OnVertex(vid) => {
51                if *vid == edge[0] {
52                    0.0
53                } else {
54                    1.0
55                }
56            }
57            Self::OnEdge(ia, ib, bcoords) => {
58                assert_eq!([*ia, *ib], edge);
59                bcoords[1]
60            }
61        }
62    }
63
64    /// Computes the point corresponding to this location.
65    pub fn to_point(self, pts: &[Point2<Real>]) -> Point2<Real> {
66        match self {
67            PolylinePointLocation::OnVertex(i) => pts[i],
68            PolylinePointLocation::OnEdge(i1, i2, bcoords) => {
69                pts[i1] * bcoords[0] + pts[i2].coords * bcoords[1]
70            }
71        }
72    }
73
74    fn from_segment_point_location(a: usize, b: usize, loc: SegmentPointLocation) -> Self {
75        match loc {
76            SegmentPointLocation::OnVertex(0) => PolylinePointLocation::OnVertex(a),
77            SegmentPointLocation::OnVertex(1) => PolylinePointLocation::OnVertex(b),
78            SegmentPointLocation::OnVertex(_) => unreachable!(),
79            SegmentPointLocation::OnEdge(bcoords) => PolylinePointLocation::OnEdge(a, b, bcoords),
80        }
81    }
82}
83
84/// Computes the intersection points of two convex polygons.
85///
86/// The resulting polygon is output vertex-by-vertex to the `out` vector.
87/// This is the same as [`convex_polygons_intersection_points_with_tolerances`] with the tolerances
88/// set to their default values.
89pub fn convex_polygons_intersection_points(
90    poly1: &[Point2<Real>],
91    poly2: &[Point2<Real>],
92    out: &mut Vec<Point2<Real>>,
93) {
94    convex_polygons_intersection_points_with_tolerances(poly1, poly2, Default::default(), out);
95}
96
97/// Computes the intersection points of two convex polygons.
98///
99/// The resulting polygon is output vertex-by-vertex to the `out` vector.
100pub fn convex_polygons_intersection_points_with_tolerances(
101    poly1: &[Point2<Real>],
102    poly2: &[Point2<Real>],
103    tolerances: PolygonIntersectionTolerances,
104    out: &mut Vec<Point2<Real>>,
105) {
106    convex_polygons_intersection_with_tolerances(poly1, poly2, tolerances, |loc1, loc2| {
107        if let Some(loc1) = loc1 {
108            out.push(loc1.to_point(poly1))
109        } else if let Some(loc2) = loc2 {
110            out.push(loc2.to_point(poly2))
111        }
112    })
113}
114
115/// Computes the intersection of two convex polygons.
116///
117/// The resulting polygon is output vertex-by-vertex to the `out` closure.
118/// This is the same as [`convex_polygons_intersection_with_tolerances`] with the tolerances
119/// set to their default values.
120pub fn convex_polygons_intersection(
121    poly1: &[Point2<Real>],
122    poly2: &[Point2<Real>],
123    out: impl FnMut(Option<PolylinePointLocation>, Option<PolylinePointLocation>),
124) {
125    convex_polygons_intersection_with_tolerances(poly1, poly2, Default::default(), out)
126}
127
128/// Computes the intersection of two convex polygons.
129///
130/// The resulting polygon is output vertex-by-vertex to the `out` closure.
131pub fn convex_polygons_intersection_with_tolerances(
132    poly1: &[Point2<Real>],
133    poly2: &[Point2<Real>],
134    tolerances: PolygonIntersectionTolerances,
135    mut out: impl FnMut(Option<PolylinePointLocation>, Option<PolylinePointLocation>),
136) {
137    // TODO: this does not handle correctly the case where the
138    // first triangle of the polygon is degenerate.
139    let rev1 = poly1.len() > 2
140        && Triangle::orientation2d(
141            &poly1[0],
142            &poly1[1],
143            &poly1[2],
144            tolerances.collinearity_epsilon,
145        ) == TriangleOrientation::Clockwise;
146    let rev2 = poly2.len() > 2
147        && Triangle::orientation2d(
148            &poly2[0],
149            &poly2[1],
150            &poly2[2],
151            tolerances.collinearity_epsilon,
152        ) == TriangleOrientation::Clockwise;
153
154    let len1 = poly1.len();
155    let len2 = poly2.len();
156
157    let mut i1 = 0; // Current index on the first polyline.
158    let mut i2 = 0; // Current index on the second polyline.
159    let mut nsteps1 = 0; // Number of times we advanced on the first polyline.
160    let mut nsteps2 = 0; // Number of times we advanced on the second polyline.
161    let mut inflag = InFlag::Unknown;
162    let mut first_point_found = false;
163
164    // Quit when both adv. indices have cycled, or one has cycled twice.
165    while (nsteps1 < len1 || nsteps2 < len2) && nsteps1 < 2 * len1 && nsteps2 < 2 * len2 {
166        let (a1, b1) = if rev1 {
167            ((len1 - i1) % len1, len1 - i1 - 1)
168        } else {
169            // Point before `i1`, and point at `i1`.
170            ((i1 + len1 - 1) % len1, i1)
171        };
172
173        let (a2, b2) = if rev2 {
174            ((len2 - i2) % len2, len2 - i2 - 1)
175        } else {
176            // Point before `i2`, and point at `i2`.
177            ((i2 + len2 - 1) % len2, i2)
178        };
179
180        let dir_edge1 = poly1[b1] - poly1[a1];
181        let dir_edge2 = poly2[b2] - poly2[a2];
182
183        // If there is an intersection, this will determine if the edge from poly2 is transitioning
184        // Left -> Right (CounterClockwise) or Right -> Left (Clockwise) relative to the edge from
185        // poly1.
186        let cross = Triangle::orientation2d(
187            &Point2::origin(),
188            &Point2::from(dir_edge1),
189            &Point2::from(dir_edge2),
190            tolerances.collinearity_epsilon,
191        );
192        // Determines if b1 is left (CounterClockwise) or right (Clockwise) of [a2, b2].
193        let a2_b2_b1 = Triangle::orientation2d(
194            &poly2[a2],
195            &poly2[b2],
196            &poly1[b1],
197            tolerances.collinearity_epsilon,
198        );
199        // Determines if b2 is left (CounterClockwise) or right (Clockwise) of [a1, b1].
200        let a1_b1_b2 = Triangle::orientation2d(
201            &poly1[a1],
202            &poly1[b1],
203            &poly2[b2],
204            tolerances.collinearity_epsilon,
205        );
206
207        // If edge1 & edge2 intersect, update inflag.
208        if let Some(inter) = utils::segments_intersection2d(
209            &poly1[a1],
210            &poly1[b1],
211            &poly2[a2],
212            &poly2[b2],
213            tolerances.collinearity_epsilon,
214        ) {
215            match inter {
216                SegmentsIntersection::Point { loc1, loc2 } => {
217                    if a2_b2_b1 != TriangleOrientation::Degenerate
218                        && a1_b1_b2 != TriangleOrientation::Degenerate
219                    {
220                        let loc1 = PolylinePointLocation::from_segment_point_location(a1, b1, loc1);
221                        let loc2 = PolylinePointLocation::from_segment_point_location(a2, b2, loc2);
222                        out(Some(loc1), Some(loc2));
223
224                        if inflag == InFlag::Unknown && !first_point_found {
225                            // This is the first point, reset the number of steps since we are
226                            // effectively starting the actual traversal now.
227                            nsteps1 = 0;
228                            nsteps2 = 0;
229                            first_point_found = true;
230                        }
231
232                        if a2_b2_b1 == TriangleOrientation::CounterClockwise {
233                            // The point b1 is left of [a2, b2] so it is inside poly2 ???
234                            inflag = InFlag::Poly1IsInside;
235                        } else if a1_b1_b2 == TriangleOrientation::CounterClockwise {
236                            // The point b2 is left of [a1, b1] so it is inside poly1 ???
237                            inflag = InFlag::Poly2IsInside;
238                        }
239                    }
240                }
241                SegmentsIntersection::Segment {
242                    first_loc1,
243                    first_loc2,
244                    second_loc1,
245                    second_loc2,
246                } => {
247                    if dir_edge1.dot(&dir_edge2) < 0.0 {
248                        // Special case: edge1 & edge2 overlap and oppositely oriented. The
249                        //               intersection is degenerate (equals to a segment). Output
250                        //               the segment and exit.
251                        let loc1 =
252                            PolylinePointLocation::from_segment_point_location(a1, b1, first_loc1);
253                        let loc2 =
254                            PolylinePointLocation::from_segment_point_location(a2, b2, first_loc2);
255                        out(Some(loc1), Some(loc2));
256
257                        let loc1 =
258                            PolylinePointLocation::from_segment_point_location(a1, b1, second_loc1);
259                        let loc2 =
260                            PolylinePointLocation::from_segment_point_location(a2, b2, second_loc2);
261                        out(Some(loc1), Some(loc2));
262                        return;
263                    }
264                }
265            }
266        }
267
268        // Special case: edge1 & edge2 parallel and separated.
269        if cross == TriangleOrientation::Degenerate
270            && a2_b2_b1 == TriangleOrientation::Clockwise
271            && a1_b1_b2 == TriangleOrientation::Clockwise
272        // TODO: should this also include any case where a2_b2_b1 and a1_b1_b2 are both different from Degenerate?
273        {
274            return;
275        }
276        // Special case: edge1 & edge2 collinear.
277        else if cross == TriangleOrientation::Degenerate
278            && a2_b2_b1 == TriangleOrientation::Degenerate
279            && a1_b1_b2 == TriangleOrientation::Degenerate
280        {
281            // Advance but do not output point.
282            if inflag == InFlag::Poly1IsInside {
283                i2 = advance(i2, &mut nsteps2, len2);
284            } else {
285                i1 = advance(i1, &mut nsteps1, len1);
286            }
287        }
288        // Generic cases.
289        else if cross == TriangleOrientation::CounterClockwise {
290            if a1_b1_b2 == TriangleOrientation::CounterClockwise {
291                if inflag == InFlag::Poly1IsInside {
292                    out(Some(PolylinePointLocation::OnVertex(b1)), None)
293                }
294                i1 = advance(i1, &mut nsteps1, len1);
295            } else {
296                if inflag == InFlag::Poly2IsInside {
297                    out(None, Some(PolylinePointLocation::OnVertex(b2)))
298                }
299                i2 = advance(i2, &mut nsteps2, len2);
300            }
301        } else {
302            // We have cross == TriangleOrientation::Clockwise.
303            if a2_b2_b1 == TriangleOrientation::CounterClockwise {
304                if inflag == InFlag::Poly2IsInside {
305                    out(None, Some(PolylinePointLocation::OnVertex(b2)))
306                }
307                i2 = advance(i2, &mut nsteps2, len2);
308            } else {
309                if inflag == InFlag::Poly1IsInside {
310                    out(Some(PolylinePointLocation::OnVertex(b1)), None)
311                }
312                i1 = advance(i1, &mut nsteps1, len1);
313            }
314        }
315    }
316
317    if !first_point_found {
318        // No intersection: test if one polygon completely encloses the other.
319        let mut orient = TriangleOrientation::Degenerate;
320        let mut ok = true;
321
322        // TODO: avoid the O(n²) complexity.
323        for a in 0..len1 {
324            for p2 in poly2 {
325                let a_minus_1 = (a + len1 - 1) % len1; // a - 1
326                let new_orient = Triangle::orientation2d(
327                    &poly1[a_minus_1],
328                    &poly1[a],
329                    p2,
330                    tolerances.collinearity_epsilon,
331                );
332
333                if orient == TriangleOrientation::Degenerate {
334                    orient = new_orient
335                } else if new_orient != orient && new_orient != TriangleOrientation::Degenerate {
336                    ok = false;
337                    break;
338                }
339            }
340        }
341
342        if ok {
343            for mut b in 0..len2 {
344                if rev2 {
345                    b = len2 - b - 1;
346                }
347                out(None, Some(PolylinePointLocation::OnVertex(b)))
348            }
349        }
350
351        let mut orient = TriangleOrientation::Degenerate;
352        let mut ok = true;
353
354        // TODO: avoid the O(n²) complexity.
355        for b in 0..len2 {
356            for p1 in poly1 {
357                let b_minus_1 = (b + len2 - 1) % len2; // = b - 1
358                let new_orient = Triangle::orientation2d(
359                    &poly2[b_minus_1],
360                    &poly2[b],
361                    p1,
362                    tolerances.collinearity_epsilon,
363                );
364
365                if orient == TriangleOrientation::Degenerate {
366                    orient = new_orient
367                } else if new_orient != orient && new_orient != TriangleOrientation::Degenerate {
368                    ok = false;
369                    break;
370                }
371            }
372        }
373
374        if ok {
375            for mut a in 0..len1 {
376                if rev1 {
377                    a = len1 - a - 1;
378                }
379                out(Some(PolylinePointLocation::OnVertex(a)), None)
380            }
381        }
382    }
383}
384
385#[inline]
386fn advance(a: usize, aa: &mut usize, n: usize) -> usize {
387    *aa += 1;
388    (a + 1) % n
389}
390
391#[derive(thiserror::Error, Debug)]
392pub enum PolygonsIntersectionError {
393    #[error("Infinite loop detected; input polygons are ill-formed.")]
394    InfiniteLoop,
395}
396
397/// Compute intersections between two polygons that may be non-convex but that must not self-intersect.
398///
399/// The input polygons are assumed to not self-intersect, and to be oriented counter-clockwise.
400///
401/// The resulting polygon is output vertex-by-vertex to the `out` closure.
402/// If two `None` are given to the `out` closure, then one connected component of the intersection
403/// polygon is complete.
404///
405/// If the polygons are known to be convex, use [`convex_polygons_intersection_points`] instead for better
406/// performances.
407pub fn polygons_intersection_points(
408    poly1: &[Point2<Real>],
409    poly2: &[Point2<Real>],
410) -> Result<Vec<Vec<Point2<Real>>>, PolygonsIntersectionError> {
411    let mut result = vec![];
412    let mut curr_poly = vec![];
413    polygons_intersection(poly1, poly2, |loc1, loc2| {
414        if let Some(loc1) = loc1 {
415            curr_poly.push(loc1.to_point(poly1))
416        } else if let Some(loc2) = loc2 {
417            curr_poly.push(loc2.to_point(poly2))
418        } else if !curr_poly.is_empty() {
419            result.push(core::mem::take(&mut curr_poly));
420        }
421    })?;
422
423    Ok(result)
424}
425
426/// Compute intersections between two polygons that may be non-convex but that must not self-intersect.
427///
428/// The input polygons are assumed to not self-intersect, and to be oriented counter-clockwise.
429///
430/// The resulting polygon is output vertex-by-vertex to the `out` closure.
431/// If two `None` are given to the `out` closure, then one connected component of the intersection
432/// polygon is complete.
433///
434/// If the polygons are known to be convex, use [`convex_polygons_intersection`] instead for better
435/// performances.
436pub fn polygons_intersection(
437    poly1: &[Point2<Real>],
438    poly2: &[Point2<Real>],
439    mut out: impl FnMut(Option<PolylinePointLocation>, Option<PolylinePointLocation>),
440) -> Result<(), PolygonsIntersectionError> {
441    let tolerances = PolygonIntersectionTolerances::default();
442
443    #[derive(Debug)]
444    struct ToTraverse {
445        poly: usize,
446        edge: EdgeId,
447    }
448
449    let (intersections, num_intersections) =
450        compute_sorted_edge_intersections(poly1, poly2, tolerances.collinearity_epsilon);
451    let mut visited = vec![false; num_intersections];
452    let segment = |eid: EdgeId, poly: &[Point2<Real>]| [poly[eid], poly[(eid + 1) % poly.len()]];
453
454    // Traverse all the intersections.
455    for inters in intersections[0].values() {
456        for inter in inters {
457            if visited[inter.id] {
458                continue;
459            }
460
461            // We found an intersection we haven’t visited yet, traverse the loop, alternating
462            // between poly1 and poly2 when reaching an intersection.
463            let [a1, b1] = segment(inter.edges[0], poly1);
464            let [a2, b2] = segment(inter.edges[1], poly2);
465            let poly_to_traverse =
466                match Triangle::orientation2d(&a1, &b1, &a2, tolerances.collinearity_epsilon) {
467                    TriangleOrientation::Clockwise => 1,
468                    TriangleOrientation::CounterClockwise => 0,
469                    TriangleOrientation::Degenerate => {
470                        match Triangle::orientation2d(
471                            &a1,
472                            &b1,
473                            &b2,
474                            tolerances.collinearity_epsilon,
475                        ) {
476                            TriangleOrientation::Clockwise => 0,
477                            TriangleOrientation::CounterClockwise => 1,
478                            TriangleOrientation::Degenerate => {
479                                log::debug!("Unhandled edge-edge overlap case.");
480                                0
481                            }
482                        }
483                    }
484                };
485
486            #[derive(Debug)]
487            enum TraversalStatus {
488                OnVertex,
489                OnIntersection(usize),
490            }
491
492            let polys = [poly1, poly2];
493            let mut to_traverse = ToTraverse {
494                poly: poly_to_traverse,
495                edge: inter.edges[poly_to_traverse],
496            };
497
498            let mut status = TraversalStatus::OnIntersection(inter.id);
499
500            for loop_id in 0.. {
501                if loop_id > poly1.len() * poly2.len() {
502                    return Err(PolygonsIntersectionError::InfiniteLoop);
503                }
504
505                let empty = Vec::new();
506                let edge_inters = intersections[to_traverse.poly]
507                    .get(&to_traverse.edge)
508                    .unwrap_or(&empty);
509
510                match status {
511                    TraversalStatus::OnIntersection(inter_id) => {
512                        let (curr_inter_pos, curr_inter) = edge_inters
513                            .iter()
514                            .enumerate()
515                            .find(|(_, inter)| inter.id == inter_id)
516                            .unwrap_or_else(|| unreachable!());
517
518                        if visited[curr_inter.id] {
519                            // We already saw this intersection: we looped back to the start of
520                            // the intersection polygon.
521                            out(None, None);
522                            break;
523                        }
524
525                        out(Some(curr_inter.locs[0]), Some(curr_inter.locs[1]));
526                        visited[curr_inter.id] = true;
527
528                        if curr_inter_pos + 1 < edge_inters.len() {
529                            // There are other intersections after this one.
530                            // Move forward to the next intersection point and move on to traversing
531                            // the other polygon.
532                            let next_inter = &edge_inters[curr_inter_pos + 1];
533                            to_traverse.poly = (to_traverse.poly + 1) % 2;
534                            to_traverse.edge = next_inter.edges[to_traverse.poly];
535                            status = TraversalStatus::OnIntersection(next_inter.id);
536                        } else {
537                            // This was the last intersection, move to the next vertex on the
538                            // same polygon.
539                            to_traverse.edge =
540                                (to_traverse.edge + 1) % polys[to_traverse.poly].len();
541                            status = TraversalStatus::OnVertex;
542                        }
543                    }
544                    TraversalStatus::OnVertex => {
545                        let location = PolylinePointLocation::OnVertex(to_traverse.edge);
546
547                        if to_traverse.poly == 0 {
548                            out(Some(location), None);
549                        } else {
550                            out(None, Some(location))
551                        };
552
553                        if let Some(first_intersection) = edge_inters.first() {
554                            // Jump on the first intersection and move on to the other polygon.
555                            to_traverse.poly = (to_traverse.poly + 1) % 2;
556                            to_traverse.edge = first_intersection.edges[to_traverse.poly];
557                            status = TraversalStatus::OnIntersection(first_intersection.id);
558                        } else {
559                            // Move forward to the next vertex/edge on the same polygon.
560                            to_traverse.edge =
561                                (to_traverse.edge + 1) % polys[to_traverse.poly].len();
562                        }
563                    }
564                }
565            }
566        }
567    }
568
569    // If there are no intersection, check if one polygon is inside the other.
570    if intersections[0].is_empty() {
571        if utils::point_in_poly2d(&poly1[0], poly2) {
572            for pt_id in 0..poly1.len() {
573                out(Some(PolylinePointLocation::OnVertex(pt_id)), None)
574            }
575            out(None, None);
576        } else if utils::point_in_poly2d(&poly2[0], poly1) {
577            for pt_id in 0..poly2.len() {
578                out(None, Some(PolylinePointLocation::OnVertex(pt_id)))
579            }
580            out(None, None);
581        }
582    }
583
584    Ok(())
585}
586
587type EdgeId = usize;
588type IntersectionId = usize;
589
590#[derive(Copy, Clone, Debug)]
591struct IntersectionPoint {
592    id: IntersectionId,
593    edges: [EdgeId; 2],
594    locs: [PolylinePointLocation; 2],
595}
596
597fn compute_sorted_edge_intersections(
598    poly1: &[Point2<Real>],
599    poly2: &[Point2<Real>],
600    eps: Real,
601) -> ([HashMap<EdgeId, Vec<IntersectionPoint>>; 2], usize) {
602    let mut inter1: HashMap<EdgeId, Vec<IntersectionPoint>> = HashMap::default();
603    let mut inter2: HashMap<EdgeId, Vec<IntersectionPoint>> = HashMap::default();
604    let mut id = 0;
605
606    // Find the intersections.
607    // TODO: this is a naive O(n²) check. Could use an acceleration structure for large polygons.
608    for i1 in 0..poly1.len() {
609        let j1 = (i1 + 1) % poly1.len();
610
611        for i2 in 0..poly2.len() {
612            let j2 = (i2 + 1) % poly2.len();
613
614            let Some(inter) =
615                utils::segments_intersection2d(&poly1[i1], &poly1[j1], &poly2[i2], &poly2[j2], eps)
616            else {
617                continue;
618            };
619
620            match inter {
621                SegmentsIntersection::Point { loc1, loc2 } => {
622                    let loc1 = PolylinePointLocation::from_segment_point_location(i1, j1, loc1);
623                    let loc2 = PolylinePointLocation::from_segment_point_location(i2, j2, loc2);
624                    let intersection = IntersectionPoint {
625                        id,
626                        edges: [i1, i2],
627                        locs: [loc1, loc2],
628                    };
629                    inter1.entry(i1).or_default().push(intersection);
630                    inter2.entry(i2).or_default().push(intersection);
631                    id += 1;
632                }
633                SegmentsIntersection::Segment { .. } => {
634                    // TODO
635                    log::debug!(
636                        "Collinear segment-segment intersections not properly handled yet."
637                    );
638                }
639            }
640        }
641    }
642
643    // Sort the intersections.
644    for inters in inter1.values_mut() {
645        inters.sort_by_key(|a| {
646            let edge = [a.edges[0], (a.edges[0] + 1) % poly1.len()];
647            OrderedFloat(a.locs[0].centered_bcoords(edge))
648        });
649    }
650
651    for inters in inter2.values_mut() {
652        inters.sort_by_key(|a| {
653            let edge = [a.edges[1], (a.edges[1] + 1) % poly2.len()];
654            OrderedFloat(a.locs[1].centered_bcoords(edge))
655        });
656    }
657
658    ([inter1, inter2], id)
659}
660
661#[cfg(all(test, feature = "dim2"))]
662mod test {
663    use crate::query::PointQuery;
664    use crate::shape::Triangle;
665    use crate::transformation::convex_polygons_intersection_points_with_tolerances;
666    use crate::transformation::polygon_intersection::PolygonIntersectionTolerances;
667    use alloc::vec::Vec;
668    use na::Point2;
669    use std::println;
670
671    #[test]
672    fn intersect_triangle_common_vertex() {
673        let tris = [
674            (
675                Triangle::new(
676                    Point2::new(-0.0008759537858568062, -2.0103871966663305),
677                    Point2::new(0.3903908709629763, -1.3421764825890266),
678                    Point2::new(1.3380817875388151, -2.0098007857739013),
679                ),
680                Triangle::new(
681                    Point2::new(0.0, -0.0),
682                    Point2::new(-0.0008759537858568062, -2.0103871966663305),
683                    Point2::new(1.9991979155226394, -2.009511242880474),
684                ),
685            ),
686            (
687                Triangle::new(
688                    Point2::new(0.7319315811016305, -0.00004046981523721891),
689                    Point2::new(2.0004914907008944, -0.00011061077714557787),
690                    Point2::new(1.1848406021956144, -0.8155712451545468),
691                ),
692                Triangle::new(
693                    Point2::new(0.0, 0.0),
694                    Point2::new(0.00011061077714557787, -2.000024893134292),
695                    Point2::new(2.0004914907008944, -0.00011061077714557787),
696                ),
697            ),
698            (
699                Triangle::new(
700                    Point2::new(-0.000049995168258705205, -0.9898801451981707),
701                    Point2::new(0.0, -0.0),
702                    Point2::new(0.583013294019752, -1.4170136900568633),
703                ),
704                Triangle::new(
705                    Point2::new(0.0, -0.0),
706                    Point2::new(-0.00010101395240669591, -2.000027389553894),
707                    Point2::new(2.000372544168497, 0.00010101395240669591),
708                ),
709            ),
710            (
711                Triangle::new(
712                    Point2::new(-0.940565646581769, -0.939804943675256),
713                    Point2::new(0.0, -0.0),
714                    Point2::new(-0.001533592366792066, -0.9283586484736431),
715                ),
716                Triangle::new(
717                    Point2::new(0.0, -0.0),
718                    Point2::new(-2.00752629829582, -2.0059026672784825),
719                    Point2::new(-0.0033081650580626698, -2.0025945022204197),
720                ),
721            ),
722        ];
723
724        for (tri1, tri2) in tris {
725            let mut inter = Vec::new();
726            let tolerances = PolygonIntersectionTolerances {
727                collinearity_epsilon: 1.0e-5,
728            };
729            convex_polygons_intersection_points_with_tolerances(
730                tri1.vertices(),
731                tri2.vertices(),
732                tolerances,
733                &mut inter,
734            );
735
736            println!("----------");
737            println!("inter: {:?}", inter);
738            println!(
739                "tri1 is in tri2: {}",
740                tri1.vertices().iter().all(|pt| tri2
741                    .project_local_point(pt, false)
742                    .is_inside_eps(pt, 1.0e-5))
743            );
744            println!(
745                "tri2 is in tri1: {}",
746                tri2.vertices().iter().all(|pt| tri1
747                    .project_local_point(pt, false)
748                    .is_inside_eps(pt, 1.0e-5))
749            );
750            for pt in &inter {
751                let proj1 = tri1.project_local_point(&pt, false);
752                let proj2 = tri2.project_local_point(&pt, false);
753                assert!(proj1.is_inside_eps(&pt, 1.0e-5));
754                assert!(proj2.is_inside_eps(&pt, 1.0e-5));
755            }
756        }
757    }
758}