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 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 Poly1IsInside,
31 Poly2IsInside,
33 Unknown,
34}
35
36#[derive(Copy, Clone, Debug, PartialEq)]
38pub enum PolylinePointLocation {
39 OnVertex(usize),
41 OnEdge(usize, usize, [Real; 2]),
43}
44
45impl PolylinePointLocation {
46 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 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
84pub 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
97pub 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
115pub 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
128pub 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 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; let mut i2 = 0; let mut nsteps1 = 0; let mut nsteps2 = 0; let mut inflag = InFlag::Unknown;
162 let mut first_point_found = false;
163
164 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 ((i1 + len1 - 1) % len1, i1)
171 };
172
173 let (a2, b2) = if rev2 {
174 ((len2 - i2) % len2, len2 - i2 - 1)
175 } else {
176 ((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 let cross = Triangle::orientation2d(
187 &Point2::origin(),
188 &Point2::from(dir_edge1),
189 &Point2::from(dir_edge2),
190 tolerances.collinearity_epsilon,
191 );
192 let a2_b2_b1 = Triangle::orientation2d(
194 &poly2[a2],
195 &poly2[b2],
196 &poly1[b1],
197 tolerances.collinearity_epsilon,
198 );
199 let a1_b1_b2 = Triangle::orientation2d(
201 &poly1[a1],
202 &poly1[b1],
203 &poly2[b2],
204 tolerances.collinearity_epsilon,
205 );
206
207 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 nsteps1 = 0;
228 nsteps2 = 0;
229 first_point_found = true;
230 }
231
232 if a2_b2_b1 == TriangleOrientation::CounterClockwise {
233 inflag = InFlag::Poly1IsInside;
235 } else if a1_b1_b2 == TriangleOrientation::CounterClockwise {
236 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 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 if cross == TriangleOrientation::Degenerate
270 && a2_b2_b1 == TriangleOrientation::Clockwise
271 && a1_b1_b2 == TriangleOrientation::Clockwise
272 {
274 return;
275 }
276 else if cross == TriangleOrientation::Degenerate
278 && a2_b2_b1 == TriangleOrientation::Degenerate
279 && a1_b1_b2 == TriangleOrientation::Degenerate
280 {
281 if inflag == InFlag::Poly1IsInside {
283 i2 = advance(i2, &mut nsteps2, len2);
284 } else {
285 i1 = advance(i1, &mut nsteps1, len1);
286 }
287 }
288 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 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 let mut orient = TriangleOrientation::Degenerate;
320 let mut ok = true;
321
322 for a in 0..len1 {
324 for p2 in poly2 {
325 let a_minus_1 = (a + len1 - 1) % len1; 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 for b in 0..len2 {
356 for p1 in poly1 {
357 let b_minus_1 = (b + len2 - 1) % len2; 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
397pub 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
426pub 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 for inters in intersections[0].values() {
456 for inter in inters {
457 if visited[inter.id] {
458 continue;
459 }
460
461 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 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 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 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 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 to_traverse.edge =
561 (to_traverse.edge + 1) % polys[to_traverse.poly].len();
562 }
563 }
564 }
565 }
566 }
567 }
568
569 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 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 log::debug!(
636 "Collinear segment-segment intersections not properly handled yet."
637 );
638 }
639 }
640 }
641 }
642
643 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}