diff options
Diffstat (limited to 'src/math/polygon/mod.rs')
| -rw-r--r-- | src/math/polygon/mod.rs | 64 |
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, ¤t_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] |
