aboutsummaryrefslogtreecommitdiff
path: root/src/geometry/polygon_intersection.rs
diff options
context:
space:
mode:
authorSébastien Crozet <developer@crozet.re>2020-08-25 22:10:25 +0200
committerSébastien Crozet <developer@crozet.re>2020-08-25 22:10:25 +0200
commit754a48b7ff6d8c58b1ee08651e60112900b60455 (patch)
tree7d777a6c003f1f5d8f8d24f533f35a95a88957fe /src/geometry/polygon_intersection.rs
downloadrapier-754a48b7ff6d8c58b1ee08651e60112900b60455.tar.gz
rapier-754a48b7ff6d8c58b1ee08651e60112900b60455.tar.bz2
rapier-754a48b7ff6d8c58b1ee08651e60112900b60455.zip
First public release of Rapier.v0.1.0
Diffstat (limited to 'src/geometry/polygon_intersection.rs')
-rw-r--r--src/geometry/polygon_intersection.rs263
1 files changed, 263 insertions, 0 deletions
diff --git a/src/geometry/polygon_intersection.rs b/src/geometry/polygon_intersection.rs
new file mode 100644
index 0000000..a2186df
--- /dev/null
+++ b/src/geometry/polygon_intersection.rs
@@ -0,0 +1,263 @@
+use na::{Point2, Real};
+
+use shape::SegmentPointLocation;
+use utils::{self, SegmentsIntersection, TriangleOrientation};
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+enum InFlag {
+ PIn,
+ QIn,
+ Unknown,
+}
+
+/// Location of a point on a polyline.
+pub enum PolylinePointLocation<N> {
+ /// Point on a vertex.
+ OnVertex(usize),
+ /// Point on an edge.
+ OnEdge(usize, usize, [N; 2]),
+}
+
+impl<N: Real> PolylinePointLocation<N> {
+ /// Computes the point corresponding to this location.
+ pub fn to_point(&self, pts: &[Point2<N>]) -> Point2<N> {
+ match self {
+ PolylinePointLocation::OnVertex(i) => pts[*i],
+ PolylinePointLocation::OnEdge(i1, i2, bcoords) => {
+ pts[*i1] * bcoords[0] + pts[*i2].coords * bcoords[1]
+ }
+ }
+ }
+
+ fn from_segment_point_location(a: usize, b: usize, loc: SegmentPointLocation<N>) -> Self {
+ match loc {
+ SegmentPointLocation::OnVertex(0) => PolylinePointLocation::OnVertex(a),
+ SegmentPointLocation::OnVertex(1) => PolylinePointLocation::OnVertex(b),
+ SegmentPointLocation::OnVertex(_) => unreachable!(),
+ SegmentPointLocation::OnEdge(bcoords) => PolylinePointLocation::OnEdge(a, b, bcoords),
+ }
+ }
+}
+
+/// Computes the intersection points of two convex polygons.
+///
+/// The resulting polygon is output vertex-by-vertex to the `out` closure.
+pub fn convex_polygons_intersection_points<N: Real>(
+ poly1: &[Point2<N>],
+ poly2: &[Point2<N>],
+ out: &mut Vec<Point2<N>>,
+) {
+ convex_polygons_intersection(poly1, poly2, |loc1, loc2| {
+ if let Some(loc1) = loc1 {
+ out.push(loc1.to_point(poly1))
+ } else if let Some(loc2) = loc2 {
+ out.push(loc2.to_point(poly2))
+ }
+ })
+}
+
+/// Computes the intersection of two convex polygons.
+///
+/// The resulting polygon is output vertex-by-vertex to the `out` closure.
+pub fn convex_polygons_intersection<N: Real>(
+ poly1: &[Point2<N>],
+ poly2: &[Point2<N>],
+ mut out: impl FnMut(Option<PolylinePointLocation<N>>, Option<PolylinePointLocation<N>>),
+) {
+ // FIXME: this does not handle correctly the case where the
+ // first triangle of the polygon is degenerate.
+ let rev1 = poly1.len() > 2
+ && utils::triangle_orientation(&poly1[0], &poly1[1], &poly1[2])
+ == TriangleOrientation::Clockwise;
+ let rev2 = poly2.len() > 2
+ && utils::triangle_orientation(&poly2[0], &poly2[1], &poly2[2])
+ == TriangleOrientation::Clockwise;
+
+ // println!("rev1: {}, rev2: {}", rev1, rev2);
+
+ let n = poly1.len();
+ let m = poly2.len();
+
+ let mut a = 0;
+ let mut b = 0;
+ let mut aa = 0;
+ let mut ba = 0;
+ let mut inflag = InFlag::Unknown;
+ let mut first_point_found = false;
+
+ // Quit when both adv. indices have cycled, or one has cycled twice.
+ while (aa < n || ba < m) && aa < 2 * n && ba < 2 * m {
+ let (a1, a2) = if rev1 {
+ ((n - a) % n, n - a - 1)
+ } else {
+ ((a + n - 1) % n, a)
+ };
+
+ let (b1, b2) = if rev2 {
+ ((m - b) % m, m - b - 1)
+ } else {
+ ((b + m - 1) % m, b)
+ };
+
+ // println!("Current indices: ({}, {}), ({}, {})", a1, a2, b1, b2);
+
+ let dir_edge1 = poly1[a2] - poly1[a1];
+ let dir_edge2 = poly2[b2] - poly2[b1];
+
+ let cross = utils::triangle_orientation(
+ &Point2::origin(),
+ &Point2::from_coordinates(dir_edge1),
+ &Point2::from_coordinates(dir_edge2),
+ );
+ let aHB = utils::triangle_orientation(&poly2[b1], &poly2[b2], &poly1[a2]);
+ let bHA = utils::triangle_orientation(&poly1[a1], &poly1[a2], &poly2[b2]);
+
+ // If edge1 & edge2 intersect, update inflag.
+ if let Some(inter) =
+ utils::segments_intersection(&poly1[a1], &poly1[a2], &poly2[b1], &poly2[b2])
+ {
+ match inter {
+ SegmentsIntersection::Point { loc1, loc2 } => {
+ let loc1 = PolylinePointLocation::from_segment_point_location(a1, a2, loc1);
+ let loc2 = PolylinePointLocation::from_segment_point_location(b1, b2, loc2);
+ out(Some(loc1), Some(loc2));
+
+ if inflag == InFlag::Unknown && !first_point_found {
+ // This is the first point.
+ aa = 0;
+ ba = 0;
+ first_point_found = true;
+ }
+
+ // Update inflag.
+ if aHB == TriangleOrientation::Counterclockwise {
+ inflag = InFlag::PIn;
+ } else if bHA == TriangleOrientation::Counterclockwise {
+ inflag = InFlag::QIn;
+ }
+ }
+ SegmentsIntersection::Segment {
+ first_loc1,
+ first_loc2,
+ second_loc1,
+ second_loc2,
+ } => {
+ // Special case: edge1 & edge2 overlap and oppositely oriented.
+ if dir_edge1.dot(&dir_edge2) < N::zero() {
+ let loc1 =
+ PolylinePointLocation::from_segment_point_location(a1, a2, first_loc1);
+ let loc2 =
+ PolylinePointLocation::from_segment_point_location(b1, b2, first_loc2);
+ out(Some(loc1), Some(loc2));
+
+ let loc1 =
+ PolylinePointLocation::from_segment_point_location(a1, a2, second_loc1);
+ let loc2 =
+ PolylinePointLocation::from_segment_point_location(b1, b2, second_loc2);
+ out(Some(loc1), Some(loc2));
+
+ return;
+ }
+ }
+ }
+ }
+
+ // Special case: edge1 & edge2 parallel and separated.
+ if cross == TriangleOrientation::Degenerate
+ && aHB == TriangleOrientation::Clockwise
+ && bHA == TriangleOrientation::Clockwise
+ {
+ return;
+ }
+ // Special case: edge1 & edge2 collinear.
+ else if cross == TriangleOrientation::Degenerate
+ && aHB == TriangleOrientation::Degenerate
+ && bHA == TriangleOrientation::Degenerate
+ {
+ // Advance but do not output point.
+ if inflag == InFlag::PIn {
+ b = advance(b, &mut ba, m);
+ } else {
+ a = advance(a, &mut aa, n);
+ }
+ }
+ // Generic cases.
+ else if cross == TriangleOrientation::Counterclockwise {
+ if bHA == TriangleOrientation::Counterclockwise {
+ if inflag == InFlag::PIn {
+ out(Some(PolylinePointLocation::OnVertex(a2)), None)
+ }
+ a = advance(a, &mut aa, n);
+ } else {
+ if inflag == InFlag::QIn {
+ out(None, Some(PolylinePointLocation::OnVertex(b2)))
+ }
+ b = advance(b, &mut ba, m);
+ }
+ } else {
+ // We have cross == TriangleOrientation::Clockwise.
+ if aHB == TriangleOrientation::Counterclockwise {
+ if inflag == InFlag::QIn {
+ out(None, Some(PolylinePointLocation::OnVertex(b2)))
+ }
+ b = advance(b, &mut ba, m);
+ } else {
+ if inflag == InFlag::PIn {
+ out(Some(PolylinePointLocation::OnVertex(a2)), None)
+ }
+ a = advance(a, &mut aa, n);
+ }
+ }
+ }
+
+ if !first_point_found {
+ // No intersection: test if one polygon completely encloses the other.
+ let mut orient = TriangleOrientation::Degenerate;
+ let mut ok = true;
+
+ for a in 0..n {
+ let a1 = (a + n - 1) % n; // a - 1
+ let new_orient = utils::triangle_orientation(&poly1[a1], &poly1[a], &poly2[0]);
+
+ if orient == TriangleOrientation::Degenerate {
+ orient = new_orient
+ } else if new_orient != orient && new_orient != TriangleOrientation::Degenerate {
+ ok = false;
+ break;
+ }
+ }
+
+ if ok {
+ for b in 0..m {
+ out(None, Some(PolylinePointLocation::OnVertex(b)))
+ }
+ }
+
+ let mut orient = TriangleOrientation::Degenerate;
+ let mut ok = true;
+
+ for b in 0..m {
+ let b1 = (b + m - 1) % m; // b - 1
+ let new_orient = utils::triangle_orientation(&poly2[b1], &poly2[b], &poly1[0]);
+
+ if orient == TriangleOrientation::Degenerate {
+ orient = new_orient
+ } else if new_orient != orient && new_orient != TriangleOrientation::Degenerate {
+ ok = false;
+ break;
+ }
+ }
+
+ if ok {
+ for a in 0..n {
+ out(Some(PolylinePointLocation::OnVertex(a)), None)
+ }
+ }
+ }
+}
+
+#[inline]
+fn advance(a: usize, aa: &mut usize, n: usize) -> usize {
+ *aa += 1;
+ (a + 1) % n
+}