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