parry3d/utils/
segments_intersection.rs1use na::Point2;
2
3use crate::math::Real;
4use crate::shape::{SegmentPointLocation, Triangle, TriangleOrientation};
5
6#[cfg(not(feature = "alloc"))]
7use na::ComplexField;
8
9pub enum SegmentsIntersection {
11 Point {
13 loc1: SegmentPointLocation,
15 loc2: SegmentPointLocation,
17 },
18 Segment {
20 first_loc1: SegmentPointLocation,
22 first_loc2: SegmentPointLocation,
24 second_loc1: SegmentPointLocation,
26 second_loc2: SegmentPointLocation,
28 },
29}
30
31pub fn segments_intersection2d(
33 a: &Point2<Real>,
34 b: &Point2<Real>,
35 c: &Point2<Real>,
36 d: &Point2<Real>,
37 epsilon: Real,
38) -> Option<SegmentsIntersection> {
39 let denom = a.x * (d.y - c.y) + b.x * (c.y - d.y) + d.x * (b.y - a.y) + c.x * (a.y - b.y);
40
41 if denom.abs() < epsilon || ulps_eq!(denom, 0.0) {
43 return parallel_intersection(a, b, c, d, epsilon);
44 }
45
46 let num = a.x * (d.y - c.y) + c.x * (a.y - d.y) + d.x * (c.y - a.y);
47 let s = num / denom;
48
49 let num = -(a.x * (c.y - b.y) + b.x * (a.y - c.y) + c.x * (b.y - a.y));
50 let t = num / denom;
51
52 if 0.0 > s || s > 1.0 || 0.0 > t || t > 1.0 {
53 None
54 } else {
55 let loc1 = if s == 0.0 {
56 SegmentPointLocation::OnVertex(0)
57 } else if s == denom {
58 SegmentPointLocation::OnVertex(1)
59 } else {
60 SegmentPointLocation::OnEdge([1.0 - s, s])
61 };
62
63 let loc2 = if t == 0.0 {
64 SegmentPointLocation::OnVertex(0)
65 } else if t == denom {
66 SegmentPointLocation::OnVertex(1)
67 } else {
68 SegmentPointLocation::OnEdge([1.0 - t, t])
69 };
70
71 Some(SegmentsIntersection::Point { loc1, loc2 })
72 }
73}
74
75fn parallel_intersection(
76 a: &Point2<Real>,
77 b: &Point2<Real>,
78 c: &Point2<Real>,
79 d: &Point2<Real>,
80 epsilon: Real,
81) -> Option<SegmentsIntersection> {
82 if Triangle::orientation2d(a, b, c, epsilon) != TriangleOrientation::Degenerate {
83 return None;
84 }
85
86 let ab_c = between(a, b, c);
87 let ab_d = between(a, b, d);
88 if let (Some(loc1), Some(loc2)) = (ab_c, ab_d) {
89 return Some(SegmentsIntersection::Segment {
90 first_loc1: loc1,
91 first_loc2: SegmentPointLocation::OnVertex(0),
92 second_loc1: loc2,
93 second_loc2: SegmentPointLocation::OnVertex(1),
94 });
95 }
96
97 let cd_a = between(c, d, a);
98 let cd_b = between(c, d, b);
99 if let (Some(loc1), Some(loc2)) = (cd_a, cd_b) {
100 return Some(SegmentsIntersection::Segment {
101 first_loc1: SegmentPointLocation::OnVertex(0),
102 first_loc2: loc1,
103 second_loc1: SegmentPointLocation::OnVertex(1),
104 second_loc2: loc2,
105 });
106 }
107
108 if let (Some(loc1), Some(loc2)) = (ab_c, cd_b) {
109 return Some(SegmentsIntersection::Segment {
110 first_loc1: loc1,
111 first_loc2: SegmentPointLocation::OnVertex(0),
112 second_loc1: SegmentPointLocation::OnVertex(1),
113 second_loc2: loc2,
114 });
115 }
116
117 if let (Some(loc1), Some(loc2)) = (ab_c, cd_a) {
118 return Some(SegmentsIntersection::Segment {
119 first_loc1: loc1,
120 first_loc2: SegmentPointLocation::OnVertex(0),
121 second_loc1: SegmentPointLocation::OnVertex(0),
122 second_loc2: loc2,
123 });
124 }
125
126 if let (Some(loc1), Some(loc2)) = (ab_d, cd_b) {
127 return Some(SegmentsIntersection::Segment {
128 first_loc1: loc1,
129 first_loc2: SegmentPointLocation::OnVertex(1),
130 second_loc1: SegmentPointLocation::OnVertex(1),
131 second_loc2: loc2,
132 });
133 }
134
135 if let (Some(loc1), Some(loc2)) = (ab_d, cd_a) {
136 return Some(SegmentsIntersection::Segment {
137 first_loc1: loc1,
138 first_loc2: SegmentPointLocation::OnVertex(1),
139 second_loc1: SegmentPointLocation::OnVertex(0),
140 second_loc2: loc2,
141 });
142 }
143
144 None
145}
146
147fn between(a: &Point2<Real>, b: &Point2<Real>, c: &Point2<Real>) -> Option<SegmentPointLocation> {
150 if a.x != b.x {
153 if a.x <= c.x && c.x <= b.x {
154 let bcoord = (c.x - a.x) / (b.x - a.x);
155 return Some(SegmentPointLocation::OnEdge([1.0 - bcoord, bcoord]));
156 } else if a.x >= c.x && c.x >= b.x {
157 let bcoord = (c.x - b.x) / (a.x - b.x);
158 return Some(SegmentPointLocation::OnEdge([bcoord, 1.0 - bcoord]));
159 }
160 } else if a.y != b.y {
161 if a.y <= c.y && c.y <= b.y {
162 let bcoord = (c.y - a.y) / (b.y - a.y);
163 return Some(SegmentPointLocation::OnEdge([1.0 - bcoord, bcoord]));
164 } else if a.y >= c.y && c.y >= b.y {
165 let bcoord = (c.y - b.y) / (a.y - b.y);
166 return Some(SegmentPointLocation::OnEdge([bcoord, 1.0 - bcoord]));
167 }
168 } else if a.x == c.x && a.y == c.y {
169 return Some(SegmentPointLocation::OnVertex(0));
170 }
171
172 None
173}