aboutsummaryrefslogtreecommitdiff
path: root/src/math/polygon/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/math/polygon/mod.rs')
-rw-r--r--src/math/polygon/mod.rs64
1 files changed, 62 insertions, 2 deletions
diff --git a/src/math/polygon/mod.rs b/src/math/polygon/mod.rs
index baa1e6d..e19e097 100644
--- a/src/math/polygon/mod.rs
+++ b/src/math/polygon/mod.rs
@@ -130,11 +130,60 @@ impl<
/* Both end-points are inside the polygon. */
+ /* In case the an endpoint of the line segment is equal to a corner of the polygon, it's
+ * not enough to merely check one edge, since if the corner is reflex, the segment may
+ * still be inside, eventhough its similar to the outwards pointing normal of one edge, but
+ * may be to the inside of the other edge.
+ */
+ let mut start_looks_inside = false;
+ let mut end_looks_inside = false;
+ /* Helper function that checks if a point p, when starting from the given corner c is in a
+ * direction so that considering both edges that are connected to c, the point is in the
+ * direction of the inside of the polygon.
+ */
+ let corner_vec_pointing_inside = |p: Vec2<T>, c: usize| {
+ let prev = (c + self.corners.len() - 1) % self.corners.len();
+ let next = (c + 1) % self.corners.len();
+
+ let last_edge_orientation =
+ math::triplet_orientation(self.corners[prev], self.corners[c], p);
+ let current_edge_orientation =
+ math::triplet_orientation(self.corners[c], self.corners[next], p);
+
+ if last_edge_orientation == TripletOrientation::Clockwise
+ && current_edge_orientation == TripletOrientation::Clockwise
+ {
+ false
+ } else {
+ true
+ }
+ };
+
+ for c in 0..self.corners.len() {
+ if line_segment.start == self.corners[c] {
+ start_looks_inside = corner_vec_pointing_inside(line_segment.end, c);
+ if !start_looks_inside {
+ return false;
+ }
+ }
+ if line_segment.end == self.corners[c] {
+ end_looks_inside = corner_vec_pointing_inside(line_segment.start, c);
+ if !end_looks_inside {
+ return false;
+ }
+ }
+ }
+
+ if start_looks_inside && end_looks_inside {
+ return true;
+ }
+
/* Check the intersections of the line segment with all polygon edges and see if it is
* piercing through any of them.
*/
for c in 0..self.corners.len() {
let next = (c + 1) % self.corners.len();
+
let current_edge = LineSegment::new(self.corners[c], self.corners[next]);
if LineSegment::intersect(&line_segment, &current_edge) {
@@ -152,8 +201,13 @@ impl<
/* If at least one of the points is on the edge, make sure, the line points
* inside of the polygon, not to the outside.
*/
- (TripletOrientation::Collinear, o) | (o, TripletOrientation::Collinear) => {
- if o == TripletOrientation::Clockwise {
+ (TripletOrientation::Collinear, o) => {
+ if !start_looks_inside && o == TripletOrientation::Clockwise {
+ return false;
+ }
+ }
+ (o, TripletOrientation::Collinear) => {
+ if !end_looks_inside && o == TripletOrientation::Clockwise {
return false;
}
}
@@ -237,6 +291,12 @@ mod test {
// Start point and endpoint outside. Line completely outside.
assert!(!polygon
.contains_line_segment(&LineSegment::new(Vec2::new(7.0, 0.), Vec2::new(7.5, 1.))));
+
+ // Start point on vertex, pointing in same dir as one of the adjacent edge normals,
+ // completely inside.
+ assert!(
+ polygon.contains_line_segment(&LineSegment::new(Vec2::new(2., 0.5), Vec2::new(4., 0.)))
+ );
}
#[test]