1use core::marker::PhantomData;
8use core::ptr::NonNull;
9
10use crate::error::{Error, Result};
11use crate::{IndexKind, Point, Rect, Segment};
12
13macro_rules! impl_line_methods {
14 ($get_ptr:expr) => {
15 #[inline]
16 pub fn rect(&self) -> Rect {
17 let ptr = $get_ptr(self);
18 let r = unsafe { tg_geom_sys::tg_line_rect(ptr) };
19 r.into()
20 }
21
22 #[inline]
23 pub fn num_points(&self) -> usize {
24 let ptr = $get_ptr(self);
25 unsafe { tg_geom_sys::tg_line_num_points(ptr) as usize }
26 }
27
28 #[inline]
29 pub fn point_at(&self, index: usize) -> Option<Point> {
30 if index >= self.num_points() {
31 None
32 } else {
33 let ptr = $get_ptr(self);
34 let p = unsafe { tg_geom_sys::tg_line_point_at(ptr, index as libc::c_int) };
35 Some(p.into())
36 }
37 }
38
39 #[inline]
40 pub fn points(&self) -> &[Point] {
41 let ptr = $get_ptr(self);
42 let raw = unsafe { tg_geom_sys::tg_line_points(ptr) };
43 if raw.is_null() {
44 &[]
45 } else {
46 unsafe { core::slice::from_raw_parts(raw as *const Point, self.num_points()) }
47 }
48 }
49
50 #[inline]
51 pub fn num_segments(&self) -> usize {
52 let ptr = $get_ptr(self);
53 unsafe { tg_geom_sys::tg_line_num_segments(ptr) as usize }
54 }
55
56 #[inline]
57 pub fn segment_at(&self, index: usize) -> Option<Segment> {
58 if index >= self.num_segments() {
59 None
60 } else {
61 let ptr = $get_ptr(self);
62 let s = unsafe { tg_geom_sys::tg_line_segment_at(ptr, index as libc::c_int) };
63 Some(s.into())
64 }
65 }
66
67 #[inline]
68 pub fn clockwise(&self) -> bool {
69 let ptr = $get_ptr(self);
70 unsafe { tg_geom_sys::tg_line_clockwise(ptr) }
71 }
72
73 #[inline]
74 pub fn length(&self) -> f64 {
75 let ptr = $get_ptr(self);
76 unsafe { tg_geom_sys::tg_line_length(ptr) }
77 }
78 };
79}
80
81pub struct Line {
83 ptr: NonNull<tg_geom_sys::tg_line>,
84}
85
86impl Line {
87 impl_line_methods!(|s: &Self| s.ptr.as_ptr());
88
89 crate::utils::impl_nearest_segment!(tg_geom_sys::tg_line_nearest_segment);
90
91 pub fn new(points: &[Point]) -> Result<Self> {
92 Self::new_ix(points, IndexKind::Default)
93 }
94
95 pub fn new_ix(points: &[Point], ix: IndexKind) -> Result<Self> {
96 let ptr = unsafe {
97 tg_geom_sys::tg_line_new_ix(
98 points.as_ptr() as *const tg_geom_sys::tg_point,
99 points.len() as libc::c_int,
100 ix.into(),
101 )
102 };
103 NonNull::new(ptr)
104 .map(|ptr| Self { ptr })
105 .ok_or(Error::OutOfMemory)
106 }
107
108 pub unsafe fn from_raw(ptr: *mut tg_geom_sys::tg_line) -> Option<Self> {
111 NonNull::new(ptr).map(|ptr| Self { ptr })
112 }
113
114 #[inline]
115 pub fn as_ptr(&self) -> *const tg_geom_sys::tg_line {
116 self.ptr.as_ptr()
117 }
118
119 pub fn into_raw(self) -> *mut tg_geom_sys::tg_line {
120 let ptr = self.ptr.as_ptr();
121 core::mem::forget(self);
122 ptr
123 }
124
125 pub fn copy(&self) -> Result<Self> {
126 let ptr = unsafe { tg_geom_sys::tg_line_copy(self.ptr.as_ptr()) };
127 NonNull::new(ptr)
128 .map(|ptr| Self { ptr })
129 .ok_or(Error::CopyFailed)
130 }
131
132 pub fn clone_ref(&self) -> Result<Self> {
133 let ptr = unsafe { tg_geom_sys::tg_line_clone(self.ptr.as_ptr()) };
134 NonNull::new(ptr)
135 .map(|ptr| Self { ptr })
136 .ok_or(Error::CopyFailed)
137 }
138
139 pub fn memsize(&self) -> usize {
140 unsafe { tg_geom_sys::tg_line_memsize(self.ptr.as_ptr()) }
141 }
142
143 pub fn index_spread(&self) -> i32 {
144 unsafe { tg_geom_sys::tg_line_index_spread(self.ptr.as_ptr()) }
145 }
146
147 pub fn index_num_levels(&self) -> i32 {
148 unsafe { tg_geom_sys::tg_line_index_num_levels(self.ptr.as_ptr()) }
149 }
150
151 pub fn index_level_num_rects(&self, level: i32) -> i32 {
152 unsafe { tg_geom_sys::tg_line_index_level_num_rects(self.ptr.as_ptr(), level) }
153 }
154
155 pub fn index_level_rect(&self, level: i32, rect_idx: i32) -> Rect {
156 let r =
157 unsafe { tg_geom_sys::tg_line_index_level_rect(self.ptr.as_ptr(), level, rect_idx) };
158 r.into()
159 }
160
161 pub fn iter_points(&self) -> impl Iterator<Item = Point> + '_ {
162 self.points().iter().copied()
163 }
164
165 pub fn iter_segments(&self) -> impl Iterator<Item = Segment> + '_ {
166 (0..self.num_segments()).map(move |i| self.segment_at(i).unwrap())
167 }
168
169 pub fn line_search<F>(&self, other: &Line, mut iter: F)
171 where
172 F: FnMut(Segment, usize, Segment, usize) -> bool,
173 {
174 unsafe extern "C" fn trampoline<F>(
175 aseg: tg_geom_sys::tg_segment, aidx: libc::c_int, bseg: tg_geom_sys::tg_segment,
176 bidx: libc::c_int, udata: *mut libc::c_void,
177 ) -> bool
178 where
179 F: FnMut(Segment, usize, Segment, usize) -> bool,
180 {
181 let iter = unsafe { &mut *(udata as *mut F) };
182 iter(aseg.into(), aidx as usize, bseg.into(), bidx as usize)
183 }
184
185 unsafe {
186 tg_geom_sys::tg_line_line_search(
187 self.ptr.as_ptr(),
188 other.as_ptr(),
189 Some(trampoline::<F>),
190 core::ptr::from_mut(&mut iter).cast(),
191 );
192 }
193 }
194}
195
196impl Drop for Line {
197 fn drop(&mut self) {
198 unsafe { tg_geom_sys::tg_line_free(self.ptr.as_ptr()) }
199 }
200}
201
202impl core::fmt::Debug for Line {
203 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
204 f.debug_struct("Line")
205 .field("num_points", &self.num_points())
206 .field("num_segments", &self.num_segments())
207 .field("rect", &self.rect())
208 .finish()
209 }
210}
211
212unsafe impl Send for Line {}
213unsafe impl Sync for Line {}
214
215#[derive(Clone, Copy)]
217pub struct LineRef<'a> {
218 ptr: *const tg_geom_sys::tg_line,
219 _marker: PhantomData<&'a ()>,
220}
221
222unsafe impl Send for LineRef<'_> {}
223unsafe impl Sync for LineRef<'_> {}
224
225impl LineRef<'_> {
226 impl_line_methods!(|s: &Self| s.ptr);
227
228 #[inline]
231 pub unsafe fn from_raw(ptr: *const tg_geom_sys::tg_line) -> Option<Self> {
232 if ptr.is_null() {
233 None
234 } else {
235 Some(Self {
236 ptr,
237 _marker: PhantomData,
238 })
239 }
240 }
241
242 #[inline]
243 pub fn as_ptr(&self) -> *const tg_geom_sys::tg_line {
244 self.ptr
245 }
246
247 pub fn to_owned(&self) -> Result<Line> {
248 let ptr = unsafe { tg_geom_sys::tg_line_copy(self.ptr) };
249 NonNull::new(ptr)
250 .map(|ptr| Line { ptr })
251 .ok_or(Error::CopyFailed)
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258
259 fn p(x: f64, y: f64) -> Point {
260 Point::new(x, y)
261 }
262
263 fn r(min_x: f64, min_y: f64, max_x: f64, max_y: f64) -> Rect {
264 Rect::from_coords(min_x, min_y, max_x, max_y)
265 }
266
267 fn u1() -> Vec<Point> {
268 vec![p(0.0, 10.0), p(0.0, 0.0), p(10.0, 0.0), p(10.0, 10.0)]
269 }
270
271 fn v1() -> Vec<Point> {
272 vec![p(0.0, 10.0), p(5.0, 0.0), p(10.0, 10.0)]
273 }
274
275 #[test]
276 fn test_line_new() {
277 let line = Line::new(&u1()).unwrap();
278 assert_eq!(line.num_points(), 4);
279 assert_eq!(line.num_segments(), 3);
280 }
281
282 #[test]
283 fn test_line_rect() {
284 let line = Line::new(&u1()).unwrap();
285 assert_eq!(line.rect(), r(0.0, 0.0, 10.0, 10.0));
286 }
287
288 #[test]
289 fn test_line_clockwise() {
290 let cw = Line::new(&[
291 p(0.0, 0.0),
292 p(0.0, 10.0),
293 p(10.0, 10.0),
294 p(10.0, 0.0),
295 p(0.0, 0.0),
296 ])
297 .unwrap();
298 assert!(cw.clockwise());
299
300 let ccw = Line::new(&[
301 p(0.0, 0.0),
302 p(10.0, 0.0),
303 p(10.0, 10.0),
304 p(0.0, 10.0),
305 p(0.0, 0.0),
306 ])
307 .unwrap();
308 assert!(!ccw.clockwise());
309
310 let partial_cw = Line::new(&[p(0.0, 0.0), p(0.0, 10.0), p(10.0, 10.0)]).unwrap();
311 assert!(partial_cw.clockwise());
312
313 let partial_ccw = Line::new(&[p(0.0, 0.0), p(10.0, 0.0), p(10.0, 10.0)]).unwrap();
314 assert!(!partial_ccw.clockwise());
315 }
316
317 #[test]
318 fn test_line_length() {
319 let line = Line::new(&u1()).unwrap();
320 assert!((line.length() - 30.0).abs() < 1e-10);
321 }
322
323 #[test]
324 fn test_line_points() {
325 let line = Line::new(&u1()).unwrap();
326 let points = line.points();
327 assert_eq!(points.len(), 4);
328
329 for (i, &p2) in points.iter().enumerate() {
330 let p1 = line.point_at(i).unwrap();
331 assert_eq!(p1, p2);
332 }
333 }
334
335 #[test]
336 fn test_line_point_at_bounds() {
337 let line = Line::new(&u1()).unwrap();
338 assert!(line.point_at(0).is_some());
339 assert!(line.point_at(3).is_some());
340 assert!(line.point_at(4).is_none());
341 assert!(line.point_at(100).is_none());
342 }
343
344 #[test]
345 fn test_line_segment_at() {
346 let line = Line::new(&u1()).unwrap();
347 assert_eq!(line.num_segments(), 3);
348
349 let seg0 = line.segment_at(0).unwrap();
350 assert_eq!(seg0.a, p(0.0, 10.0));
351 assert_eq!(seg0.b, p(0.0, 0.0));
352
353 assert!(line.segment_at(3).is_none());
354 }
355
356 #[test]
357 fn test_line_copy() {
358 let line = Line::new(&u1()).unwrap();
359 let copy = line.copy().unwrap();
360 assert_eq!(line.num_points(), copy.num_points());
361 assert_eq!(line.length(), copy.length());
362 }
363
364 #[test]
365 fn test_line_clone_ref() {
366 let line = Line::new(&u1()).unwrap();
367 let cloned = line.clone_ref().unwrap();
368 assert_eq!(line.num_points(), cloned.num_points());
369 }
370
371 #[test]
372 fn test_line_memsize() {
373 let line = Line::new(&u1()).unwrap();
374 assert!(line.memsize() > 0);
375 }
376
377 #[test]
378 fn test_line_iter_points() {
379 let line = Line::new(&u1()).unwrap();
380 let points: Vec<Point> = line.iter_points().collect();
381 assert_eq!(points.len(), 4);
382 }
383
384 #[test]
385 fn test_line_iter_segments() {
386 let line = Line::new(&u1()).unwrap();
387 let segments: Vec<Segment> = line.iter_segments().collect();
388 assert_eq!(segments.len(), 3);
389 }
390
391 #[test]
392 fn test_line_line_search() {
393 let line1 = Line::new(&[p(0.0, 0.0), p(10.0, 10.0)]).unwrap();
394 let line2 = Line::new(&[p(0.0, 10.0), p(10.0, 0.0)]).unwrap();
395
396 let mut found = false;
397 line1.line_search(&line2, |_seg1, _idx1, _seg2, _idx2| {
398 found = true;
399 false
400 });
401 assert!(found);
402 }
403
404 #[test]
405 fn test_line_line_search_no_intersection() {
406 let line1 = Line::new(&[p(0.0, 0.0), p(1.0, 1.0)]).unwrap();
407 let line2 = Line::new(&[p(10.0, 10.0), p(11.0, 11.0)]).unwrap();
408
409 let mut count = 0;
410 line1.line_search(&line2, |_seg1, _idx1, _seg2, _idx2| {
411 count += 1;
412 true
413 });
414 assert_eq!(count, 0);
415 }
416
417 #[test]
418 fn test_line_debug() {
419 let line = Line::new(&u1()).unwrap();
420 let debug_str = format!("{line:?}");
421 assert!(debug_str.contains("Line"));
422 assert!(debug_str.contains("num_points"));
423 }
424
425 #[test]
426 fn test_line_send_sync() {
427 fn assert_send<T: Send>() {}
428 fn assert_sync<T: Sync>() {}
429 assert_send::<Line>();
430 assert_sync::<Line>();
431 }
432
433 #[test]
434 fn test_line_index_kinds() {
435 let points = u1();
436 let line_default = Line::new(&points).unwrap();
437 let line_none = Line::new_ix(&points, IndexKind::None).unwrap();
438 let line_natural = Line::new_ix(&points, IndexKind::Natural).unwrap();
439
440 assert_eq!(line_default.num_points(), line_none.num_points());
441 assert_eq!(line_default.num_points(), line_natural.num_points());
442 }
443
444 #[test]
445 fn test_line_raw_pointer() {
446 let line = Line::new(&u1()).unwrap();
447 let num_points = line.num_points();
448 let ptr = line.into_raw();
449 let recovered = unsafe { Line::from_raw(ptr).unwrap() };
450 assert_eq!(recovered.num_points(), num_points);
451 }
452
453 #[test]
454 fn test_line_v_shape() {
455 let line = Line::new(&v1()).unwrap();
456 assert_eq!(line.num_points(), 3);
457 assert_eq!(line.num_segments(), 2);
458 assert_eq!(line.point_at(0).unwrap(), p(0.0, 10.0));
459 assert_eq!(line.point_at(1).unwrap(), p(5.0, 0.0));
460 assert_eq!(line.point_at(2).unwrap(), p(10.0, 10.0));
461 }
462
463 fn point_distance_rect(point: Point, rect: Rect) -> f64 {
464 let x = point.x.clamp(rect.min.x, rect.max.x);
465 let y = point.y.clamp(rect.min.y, rect.max.y);
466 let dx = point.x - x;
467 let dy = point.y - y;
468 (dx * dx + dy * dy).sqrt()
469 }
470
471 fn point_distance_segment(point: Point, seg: Segment) -> f64 {
472 let dx = seg.b.x - seg.a.x;
473 let dy = seg.b.y - seg.a.y;
474 let len_sq = dx * dx + dy * dy;
475
476 if len_sq == 0.0 {
477 let dx = point.x - seg.a.x;
478 let dy = point.y - seg.a.y;
479 return (dx * dx + dy * dy).sqrt();
480 }
481
482 let t = ((point.x - seg.a.x) * dx + (point.y - seg.a.y) * dy) / len_sq;
483 let t = t.clamp(0.0, 1.0);
484
485 let closest_x = seg.a.x + t * dx;
486 let closest_y = seg.a.y + t * dy;
487 let dx = point.x - closest_x;
488 let dy = point.y - closest_y;
489 (dx * dx + dy * dy).sqrt()
490 }
491
492 #[test]
493 fn test_line_nearest_segment_basic() {
494 let line = Line::new_ix(&u1(), IndexKind::Natural).unwrap();
495 let test_point = p(5.0, 5.0);
496 let num_segments = line.num_segments();
497
498 let mut visited = vec![false; num_segments];
499 let mut last_dist = -1.0f64;
500 let mut count = 0usize;
501
502 let completed = line.nearest_segment(
503 |rect| {
504 let dist = point_distance_rect(test_point, rect);
505 (dist, false)
506 },
507 |seg| {
508 let dist = point_distance_segment(test_point, seg);
509 (dist, false)
510 },
511 |seg, dist, idx| {
512 assert!(idx < num_segments);
513 assert!(count == 0 || dist >= last_dist - 1e-10);
514 assert!(!visited[idx]);
515 visited[idx] = true;
516
517 let original_seg = line.segment_at(idx).unwrap();
518 assert_eq!(seg.a, original_seg.a);
519 assert_eq!(seg.b, original_seg.b);
520
521 last_dist = dist;
522 count += 1;
523 true
524 },
525 );
526
527 assert!(completed);
528 assert_eq!(count, num_segments);
529
530 let visited_count = visited.iter().filter(|&&v| v).count();
531 assert_eq!(visited_count, count);
532 }
533
534 #[test]
535 fn test_line_nearest_segment_early_stop() {
536 let line = Line::new_ix(&u1(), IndexKind::Natural).unwrap();
537 let test_point = p(5.0, 5.0);
538 let stop_at = 2;
539 let mut count = 0;
540
541 let success = line.nearest_segment(
542 |rect| (point_distance_rect(test_point, rect), false),
543 |seg| (point_distance_segment(test_point, seg), false),
544 |_seg, _dist, _idx| {
545 count += 1;
546 count < stop_at
547 },
548 );
549
550 assert!(success);
551 assert_eq!(count, stop_at);
552 assert!(count < line.num_segments());
553 }
554
555 #[test]
556 fn test_line_nearest_segment_no_index() {
557 let line = Line::new_ix(&u1(), IndexKind::None).unwrap();
558 let test_point = p(5.0, 5.0);
559 let mut count = 0;
560
561 let completed = line.nearest_segment(
562 |rect| (point_distance_rect(test_point, rect), false),
563 |seg| (point_distance_segment(test_point, seg), false),
564 |_seg, _dist, _idx| {
565 count += 1;
566 true
567 },
568 );
569
570 assert!(completed);
571 assert_eq!(count, line.num_segments());
572 }
573
574 #[test]
575 fn test_line_nearest_segment_closest_first() {
576 let line = Line::new(&u1()).unwrap();
577 let test_point = p(5.0, 0.1);
578
579 let mut first_dist: Option<f64> = None;
580
581 line.nearest_segment(
582 |rect| (point_distance_rect(test_point, rect), false),
583 |seg| (point_distance_segment(test_point, seg), false),
584 |_seg, dist, _idx| {
585 if first_dist.is_none() {
586 first_dist = Some(dist);
587 }
588 false
589 },
590 );
591
592 let dist = first_dist.expect("Should find at least one segment");
593 assert!(dist < 0.2);
594 }
595}