1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum Orientation {
12 Collinear,
13 Clockwise,
14 CounterClockwise,
15}
16
17pub fn orientation(p: [f64; 2], q: [f64; 2], r: [f64; 2]) -> Orientation {
19 let val = (q[1] - p[1]) * (r[0] - q[0]) - (q[0] - p[0]) * (r[1] - q[1]);
20 if val.abs() < 1e-12 {
22 Orientation::Collinear
23 } else if val > 0.0 {
24 Orientation::Clockwise
25 } else {
26 Orientation::CounterClockwise
27 }
28}
29
30pub fn on_segment(p: [f64; 2], q: [f64; 2], r: [f64; 2]) -> bool {
32 q[0] <= p[0].max(r[0])
33 && q[0] >= p[0].min(r[0])
34 && q[1] <= p[1].max(r[1])
35 && q[1] >= p[1].min(r[1])
36}
37
38pub fn segments_intersect(p1: [f64; 2], q1: [f64; 2], p2: [f64; 2], q2: [f64; 2]) -> bool {
43 let o1 = orientation(p1, q1, p2);
44 let o2 = orientation(p1, q1, q2);
45 let o3 = orientation(p2, q2, p1);
46 let o4 = orientation(p2, q2, q1);
47
48 if o1 != o2 && o3 != o4 {
50 return true;
51 }
52
53 if o1 == Orientation::Collinear && on_segment(p1, p2, q1) {
55 return true;
56 }
57 if o2 == Orientation::Collinear && on_segment(p1, q2, q1) {
58 return true;
59 }
60 if o3 == Orientation::Collinear && on_segment(p2, p1, q2) {
61 return true;
62 }
63 if o4 == Orientation::Collinear && on_segment(p2, q1, q2) {
64 return true;
65 }
66
67 false
68}
69
70pub fn point_on_segment(pt: [f64; 2], seg_a: [f64; 2], seg_b: [f64; 2]) -> bool {
72 let cross =
74 (pt[0] - seg_a[0]) * (seg_b[1] - seg_a[1]) - (pt[1] - seg_a[1]) * (seg_b[0] - seg_a[0]);
75 if cross.abs() > 1e-10 {
76 return false;
77 }
78 pt[0] >= seg_a[0].min(seg_b[0]) - 1e-10
80 && pt[0] <= seg_a[0].max(seg_b[0]) + 1e-10
81 && pt[1] >= seg_a[1].min(seg_b[1]) - 1e-10
82 && pt[1] <= seg_a[1].max(seg_b[1]) + 1e-10
83}
84
85pub fn point_on_ring_boundary(pt: [f64; 2], ring: &[[f64; 2]]) -> bool {
87 if ring.len() < 2 {
88 return false;
89 }
90 for i in 0..ring.len() - 1 {
91 if point_on_segment(pt, ring[i], ring[i + 1]) {
92 return true;
93 }
94 }
95 if ring.first() != ring.last()
97 && let (Some(&first), Some(&last)) = (ring.first(), ring.last())
98 && point_on_segment(pt, last, first)
99 {
100 return true;
101 }
102 false
103}
104
105pub fn point_to_segment_dist_sq(pt: [f64; 2], seg_a: [f64; 2], seg_b: [f64; 2]) -> f64 {
109 let dx = seg_b[0] - seg_a[0];
110 let dy = seg_b[1] - seg_a[1];
111 let len_sq = dx * dx + dy * dy;
112
113 if len_sq < 1e-20 {
114 let ex = pt[0] - seg_a[0];
116 let ey = pt[1] - seg_a[1];
117 return ex * ex + ey * ey;
118 }
119
120 let t = ((pt[0] - seg_a[0]) * dx + (pt[1] - seg_a[1]) * dy) / len_sq;
122 let t = t.clamp(0.0, 1.0);
123
124 let proj_x = seg_a[0] + t * dx;
125 let proj_y = seg_a[1] + t * dy;
126
127 let ex = pt[0] - proj_x;
128 let ey = pt[1] - proj_y;
129 ex * ex + ey * ey
130}
131
132pub fn segment_to_segment_dist_sq(a1: [f64; 2], a2: [f64; 2], b1: [f64; 2], b2: [f64; 2]) -> f64 {
134 if segments_intersect(a1, a2, b1, b2) {
135 return 0.0;
136 }
137 let d1 = point_to_segment_dist_sq(a1, b1, b2);
139 let d2 = point_to_segment_dist_sq(a2, b1, b2);
140 let d3 = point_to_segment_dist_sq(b1, a1, a2);
141 let d4 = point_to_segment_dist_sq(b2, a1, a2);
142 d1.min(d2).min(d3).min(d4)
143}
144
145pub fn ring_edges(ring: &[[f64; 2]]) -> Vec<([f64; 2], [f64; 2])> {
147 if ring.len() < 2 {
148 return Vec::new();
149 }
150 let mut edges = Vec::with_capacity(ring.len());
151 for i in 0..ring.len() - 1 {
152 edges.push((ring[i], ring[i + 1]));
153 }
154 if ring.first() != ring.last()
156 && let (Some(&first), Some(&last)) = (ring.first(), ring.last())
157 {
158 edges.push((last, first));
159 }
160 edges
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166
167 #[test]
168 fn segments_cross() {
169 assert!(segments_intersect(
170 [0.0, 0.0],
171 [10.0, 10.0],
172 [0.0, 10.0],
173 [10.0, 0.0]
174 ));
175 }
176
177 #[test]
178 fn segments_parallel_no_cross() {
179 assert!(!segments_intersect(
180 [0.0, 0.0],
181 [10.0, 0.0],
182 [0.0, 1.0],
183 [10.0, 1.0]
184 ));
185 }
186
187 #[test]
188 fn segments_collinear_overlap() {
189 assert!(segments_intersect(
190 [0.0, 0.0],
191 [5.0, 0.0],
192 [3.0, 0.0],
193 [8.0, 0.0]
194 ));
195 }
196
197 #[test]
198 fn segments_collinear_no_overlap() {
199 assert!(!segments_intersect(
200 [0.0, 0.0],
201 [2.0, 0.0],
202 [3.0, 0.0],
203 [5.0, 0.0]
204 ));
205 }
206
207 #[test]
208 fn segments_endpoint_touch() {
209 assert!(segments_intersect(
210 [0.0, 0.0],
211 [5.0, 5.0],
212 [5.0, 5.0],
213 [10.0, 0.0]
214 ));
215 }
216
217 #[test]
218 fn point_on_segment_middle() {
219 assert!(point_on_segment([5.0, 5.0], [0.0, 0.0], [10.0, 10.0]));
220 }
221
222 #[test]
223 fn point_on_segment_endpoint() {
224 assert!(point_on_segment([0.0, 0.0], [0.0, 0.0], [10.0, 10.0]));
225 }
226
227 #[test]
228 fn point_off_segment() {
229 assert!(!point_on_segment([5.0, 6.0], [0.0, 0.0], [10.0, 10.0]));
230 }
231
232 #[test]
233 fn point_on_ring_edge() {
234 let ring = vec![
235 [0.0, 0.0],
236 [10.0, 0.0],
237 [10.0, 10.0],
238 [0.0, 10.0],
239 [0.0, 0.0],
240 ];
241 assert!(point_on_ring_boundary([5.0, 0.0], &ring)); assert!(point_on_ring_boundary([0.0, 5.0], &ring)); assert!(!point_on_ring_boundary([5.0, 5.0], &ring)); }
245
246 #[test]
247 fn point_on_ring_vertex() {
248 let ring = vec![
249 [0.0, 0.0],
250 [10.0, 0.0],
251 [10.0, 10.0],
252 [0.0, 10.0],
253 [0.0, 0.0],
254 ];
255 assert!(point_on_ring_boundary([0.0, 0.0], &ring));
256 assert!(point_on_ring_boundary([10.0, 10.0], &ring));
257 }
258
259 #[test]
260 fn point_to_segment_perpendicular() {
261 let d = point_to_segment_dist_sq([5.0, 1.0], [0.0, 0.0], [10.0, 0.0]);
262 assert!((d - 1.0).abs() < 1e-10); }
264
265 #[test]
266 fn point_to_segment_endpoint() {
267 let d = point_to_segment_dist_sq([12.0, 0.0], [0.0, 0.0], [10.0, 0.0]);
268 assert!((d - 4.0).abs() < 1e-10); }
270
271 #[test]
272 fn segment_to_segment_parallel() {
273 let d = segment_to_segment_dist_sq([0.0, 0.0], [10.0, 0.0], [0.0, 3.0], [10.0, 3.0]);
274 assert!((d - 9.0).abs() < 1e-10);
275 }
276
277 #[test]
278 fn segment_to_segment_crossing() {
279 let d = segment_to_segment_dist_sq([0.0, 0.0], [10.0, 10.0], [0.0, 10.0], [10.0, 0.0]);
280 assert!(d < 1e-10);
281 }
282
283 #[test]
284 fn ring_edges_closed() {
285 let ring = vec![
286 [0.0, 0.0],
287 [10.0, 0.0],
288 [10.0, 10.0],
289 [0.0, 10.0],
290 [0.0, 0.0],
291 ];
292 let edges = ring_edges(&ring);
293 assert_eq!(edges.len(), 4);
294 }
295}