diff options
Diffstat (limited to 'src/math/line_segment.rs')
| -rw-r--r-- | src/math/line_segment.rs | 51 |
1 files changed, 49 insertions, 2 deletions
diff --git a/src/math/line_segment.rs b/src/math/line_segment.rs index b496787..204cf0c 100644 --- a/src/math/line_segment.rs +++ b/src/math/line_segment.rs @@ -1,3 +1,6 @@ +//! A line segment is like a line, but with a start and an end, with the line only being between +//! those two. + use super::{Rect, Surface, TripletOrientation, Vec2}; use alga::general::{ClosedDiv, ClosedMul, ClosedSub}; use nalgebra::{RealField, Scalar}; @@ -5,6 +8,7 @@ use num_traits::Zero; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; +#[allow(missing_docs)] #[derive(Debug, Clone, Deserialize, Serialize)] pub struct LineSegment<T: Scalar + Copy> { pub start: Vec2<T>, @@ -12,6 +16,7 @@ pub struct LineSegment<T: Scalar + Copy> { } impl<T: Scalar + Copy> LineSegment<T> { + /// Create a new line segment from `start` to `end` pub fn new(start: Vec2<T>, end: Vec2<T>) -> Self { Self { start, end } } @@ -92,6 +97,9 @@ impl<T: Scalar + Copy> LineSegment<T> { false } + /// Try to find the the point where the two line segments intersect. If they do not intersect, + /// `None` is returned. If the lines are parallel and intersect (at least part of a line is on + /// a part of the other line), inside that region is returned. pub fn intersection(line_a: &LineSegment<T>, line_b: &LineSegment<T>) -> Option<Vec2<T>> where T: ClosedSub + ClosedMul + ClosedDiv + Zero + RealField, @@ -104,10 +112,26 @@ impl<T: Scalar + Copy> LineSegment<T> { let dby = line_b.start.y - line_b.end.y; // Calculate determinant to see, if the lines are parallel or not - // TODO: Collinearity check? let d = (dax * dby) - (day * dbx); if d == T::zero() { - None + // The two line segments are parallel, check if one may be on the other. + if super::triplet_orientation(line_a.start, line_a.end, line_b.start) == TripletOrientation::Collinear + && super::triplet_orientation(line_a.start, line_a.end, line_b.end) == TripletOrientation::Collinear + { + if line_a.contains_collinear(line_b.start) { + Some(line_b.start) + } else if line_a.contains_collinear(line_b.end) { + Some(line_b.end) + } else if line_b.contains_collinear(line_a.start) { + Some(line_a.start) + } else if line_b.contains_collinear(line_a.end) { + Some(line_a.end) + } else { + None + } + } else { + None + } } else { let ad = (line_a.start.x * line_a.end.y) - (line_a.start.y * line_a.end.x); let bd = (line_b.start.x * line_b.end.y) - (line_b.start.y * line_b.end.x); @@ -190,4 +214,27 @@ mod test { assert!(!segment.contains_collinear(Vec2::new(3., 3.))); assert!(!segment.contains_collinear(Vec2::new(-1., 0.))); } + + #[test] + fn line_intersection() { + let segment_a = LineSegment::new(Vec2::new(-1., -1.), Vec2::new(1., 1.)); + let segment_b = LineSegment::new(Vec2::new(1., -1.), Vec2::new(-1., 1.)); + let segment_c = LineSegment::new(Vec2::new(1., -1.), Vec2::new(2., 2.)); + + assert!(LineSegment::intersect(&segment_a, &segment_b)); + assert!(LineSegment::intersect(&segment_b, &segment_c)); + assert!(!LineSegment::intersect(&segment_a, &segment_c)); + assert!(LineSegment::intersect(&segment_a, &segment_a)); + + assert_eq!( + LineSegment::intersection(&segment_a, &segment_b), + Some(Vec2::new(0., 0.)) + ); + assert_eq!( + LineSegment::intersection(&segment_b, &segment_c), + Some(Vec2::new(1., -1.)) + ); + assert_eq!(LineSegment::intersection(&segment_a, &segment_c), None); + assert!(LineSegment::intersection(&segment_a, &segment_a).is_some()); + } } |
