geometry_overlay/
line_intersection.rs1use geometry_coords::CoordinateScalar;
8use geometry_model::Segment;
9use geometry_trait::{Point, PointMut, Segment as SegmentTrait, segment_end, segment_start};
10
11use crate::operation::OverlayError;
12use crate::predicate::{SegmentIntersection, segment_intersection};
13use crate::turn::{Method, classify::refine_touch};
14
15#[derive(Debug, Clone, Copy, PartialEq)]
17pub enum LineIntersection<P: Point> {
18 SinglePoint {
20 intersection: P,
22 is_proper: bool,
24 },
25 Collinear {
27 intersection: Segment<P>,
29 },
30}
31
32#[inline]
43#[must_use = "line intersection can fail and its result should be used"]
44pub fn line_intersection<S, P>(
45 first: &S,
46 second: &S,
47) -> Result<Option<LineIntersection<P>>, OverlayError>
48where
49 S: SegmentTrait<Point = P>,
50 P: PointMut + Default,
51 P::Scalar: CoordinateScalar + Into<f64> + PartialEq,
52{
53 match segment_intersection(first, second) {
54 SegmentIntersection::Disjoint => Ok(None),
55 SegmentIntersection::OutOfRange => Err(OverlayError::Unsupported),
56 SegmentIntersection::Single(intersection) => {
57 let first_start = segment_start(first);
58 let first_end = segment_end(first);
59 let second_start = segment_start(second);
60 let second_end = segment_end(second);
61 let is_proper = refine_touch(
62 &intersection,
63 &first_start,
64 &first_end,
65 &second_start,
66 &second_end,
67 ) == Method::Crosses;
68 Ok(Some(LineIntersection::SinglePoint {
69 intersection,
70 is_proper,
71 }))
72 }
73 SegmentIntersection::Collinear { from, to } => Ok(Some(LineIntersection::Collinear {
74 intersection: Segment::new(from, to),
75 })),
76 }
77}
78
79#[cfg(test)]
80mod tests {
81 use super::{LineIntersection, line_intersection};
82 use geometry_cs::Cartesian;
83 use geometry_model::{Point2D, Segment};
84
85 type P = Point2D<f64, Cartesian>;
86
87 #[test]
88 fn distinguishes_proper_crossing_from_endpoint_touch() {
89 let diagonal = Segment::new(P::new(0.0, 0.0), P::new(2.0, 2.0));
90 let crossing = Segment::new(P::new(0.0, 2.0), P::new(2.0, 0.0));
91 let touching = Segment::new(P::new(2.0, 2.0), P::new(3.0, 2.0));
92
93 assert_eq!(
94 line_intersection(&diagonal, &crossing),
95 Ok(Some(LineIntersection::SinglePoint {
96 intersection: P::new(1.0, 1.0),
97 is_proper: true,
98 }))
99 );
100 assert_eq!(
101 line_intersection(&diagonal, &touching),
102 Ok(Some(LineIntersection::SinglePoint {
103 intersection: P::new(2.0, 2.0),
104 is_proper: false,
105 }))
106 );
107 }
108}