1use ndarray::{Array2, ArrayView2};
32use num_traits::Float;
33
34#[allow(dead_code)]
72pub fn point_in_polygon<T: Float>(point: &[T], polygon: &ArrayView2<T>) -> bool {
73 let x = point[0];
74 let y = point[1];
75 let n = polygon.shape()[0];
76
77 if n < 3 {
78 return false; }
80
81 let epsilon = T::from(1e-10).unwrap();
83 if point_on_boundary(point, polygon, epsilon) {
84 return true;
85 }
86
87 let mut inside = false;
89
90 for i in 0..n {
91 let j = (i + 1) % n;
92
93 let vi_y = polygon[[i, 1]];
94 let vj_y = polygon[[j, 1]];
95
96 if ((vi_y <= y) && (vj_y > y)) || ((vi_y > y) && (vj_y <= y)) {
98 let vi_x = polygon[[i, 0]];
100 let vj_x = polygon[[j, 0]];
101
102 let slope = (vj_x - vi_x) / (vj_y - vi_y);
104 let intersect_x = vi_x + (y - vi_y) * slope;
105
106 if x < intersect_x {
107 inside = !inside;
108 }
109 }
110 }
111
112 inside
113}
114
115#[allow(dead_code)]
153pub fn point_on_boundary<T: Float>(point: &[T], polygon: &ArrayView2<T>, epsilon: T) -> bool {
154 let x = point[0];
155 let y = point[1];
156 let n = polygon.shape()[0];
157
158 if n < 2 {
159 return false;
160 }
161
162 for i in 0..n {
163 let j = (i + 1) % n;
164
165 let x1 = polygon[[i, 0]];
166 let y1 = polygon[[i, 1]];
167 let x2 = polygon[[j, 0]];
168 let y2 = polygon[[j, 1]];
169
170 let length_squared = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
172
173 if length_squared.is_zero() {
174 let dist = ((x - x1) * (x - x1) + (y - y1) * (y - y1)).sqrt();
176 if dist < epsilon {
177 return true;
178 }
179 } else {
180 let t = ((x - x1) * (x2 - x1) + (y - y1) * (y2 - y1)) / length_squared;
182
183 if t < T::zero() {
184 let dist = ((x - x1) * (x - x1) + (y - y1) * (y - y1)).sqrt();
186 if dist < epsilon {
187 return true;
188 }
189 } else if t > T::one() {
190 let dist = ((x - x2) * (x - x2) + (y - y2) * (y - y2)).sqrt();
192 if dist < epsilon {
193 return true;
194 }
195 } else {
196 let projection_x = x1 + t * (x2 - x1);
198 let projection_y = y1 + t * (y2 - y1);
199 let dist = ((x - projection_x) * (x - projection_x)
200 + (y - projection_y) * (y - projection_y))
201 .sqrt();
202 if dist < epsilon {
203 return true;
204 }
205 }
206 }
207 }
208
209 false
210}
211
212#[allow(dead_code)]
242pub fn polygon_area<T: Float>(polygon: &ArrayView2<T>) -> T {
243 let n = polygon.shape()[0];
244
245 if n < 3 {
246 return T::zero(); }
248
249 let mut area = T::zero();
250
251 for i in 0..n {
252 let j = (i + 1) % n;
253
254 let xi = polygon[[i, 0]];
255 let yi = polygon[[i, 1]];
256 let xj = polygon[[j, 0]];
257 let yj = polygon[[j, 1]];
258
259 area = area + (xi * yj - xj * yi);
260 }
261
262 (area / (T::one() + T::one())).abs()
263}
264
265#[allow(dead_code)]
294pub fn polygon_centroid<T: Float>(polygon: &ArrayView2<T>) -> Vec<T> {
295 let n = polygon.shape()[0];
296
297 if n < 3 {
298 let mut x_sum = T::zero();
300 let mut y_sum = T::zero();
301
302 for i in 0..n {
303 x_sum = x_sum + polygon[[i, 0]];
304 y_sum = y_sum + polygon[[i, 1]];
305 }
306
307 if n > 0 {
308 return vec![x_sum / T::from(n).unwrap(), y_sum / T::from(n).unwrap()];
309 } else {
310 return vec![T::zero(), T::zero()];
311 }
312 }
313
314 let mut cx = T::zero();
315 let mut cy = T::zero();
316 let mut area = T::zero();
317
318 for i in 0..n {
319 let j = (i + 1) % n;
320
321 let xi = polygon[[i, 0]];
322 let yi = polygon[[i, 1]];
323 let xj = polygon[[j, 0]];
324 let yj = polygon[[j, 1]];
325
326 let cross = xi * yj - xj * yi;
327
328 cx = cx + (xi + xj) * cross;
329 cy = cy + (yi + yj) * cross;
330 area = area + cross;
331 }
332
333 area = area / (T::one() + T::one());
334
335 if area.is_zero() {
336 let mut x_sum = T::zero();
338 let mut y_sum = T::zero();
339
340 for i in 0..n {
341 x_sum = x_sum + polygon[[i, 0]];
342 y_sum = y_sum + polygon[[i, 1]];
343 }
344
345 return vec![x_sum / T::from(n).unwrap(), y_sum / T::from(n).unwrap()];
346 }
347
348 let six = T::from(6).unwrap();
349 cx = cx / (six * area);
350 cy = cy / (six * area);
351
352 vec![cx.abs(), cy.abs()]
355}
356
357#[allow(dead_code)]
394pub fn polygon_contains_polygon<T: Float>(
395 polygon_a: &ArrayView2<T>,
396 polygon_b: &ArrayView2<T>,
397) -> bool {
398 if polygon_a.shape() == polygon_b.shape() {
400 let n = polygon_a.shape()[0];
401 let mut same = true;
402
403 for i in 0..n {
404 if polygon_a[[i, 0]] != polygon_b[[i, 0]] || polygon_a[[i, 1]] != polygon_b[[i, 1]] {
405 same = false;
406 break;
407 }
408 }
409
410 if same {
411 return true;
412 }
413 }
414
415 let n_b = polygon_b.shape()[0];
416
417 for i in 0..n_b {
419 let point = [polygon_b[[i, 0]], polygon_b[[i, 1]]];
420 if !point_in_polygon(&point, polygon_a) {
421 return false;
422 }
423 }
424
425 if polygon_a.shape() != polygon_b.shape() {
427 let n_a = polygon_a.shape()[0];
428
429 for i in 0..n_a {
431 let j = (i + 1) % n_a;
432 let a1 = [polygon_a[[i, 0]], polygon_a[[i, 1]]];
433 let a2 = [polygon_a[[j, 0]], polygon_a[[j, 1]]];
434
435 for k in 0..n_b {
436 let l = (k + 1) % n_b;
437 let b1 = [polygon_b[[k, 0]], polygon_b[[k, 1]]];
438 let b2 = [polygon_b[[l, 0]], polygon_b[[l, 1]]];
439
440 if segments_intersect(&a1, &a2, &b1, &b2)
442 && !segments_overlap(&a1, &a2, &b1, &b2, T::epsilon())
443 && !point_on_boundary(&a1, polygon_b, T::epsilon())
444 && !point_on_boundary(&a2, polygon_b, T::epsilon())
445 && !point_on_boundary(&b1, polygon_a, T::epsilon())
446 && !point_on_boundary(&b2, polygon_a, T::epsilon())
447 {
448 return false;
449 }
450 }
451 }
452 }
453
454 true
455}
456
457#[allow(dead_code)]
468fn segments_intersect<T: Float>(a1: &[T], a2: &[T], b1: &[T], b2: &[T]) -> bool {
469 let orientation = |p: &[T], q: &[T], r: &[T]| -> i32 {
475 let val = (q[1] - p[1]) * (r[0] - q[0]) - (q[0] - p[0]) * (r[1] - q[1]);
476
477 if val < T::zero() {
478 return 1; } else if val > T::zero() {
480 return 2; }
482
483 0 };
485
486 let on_segment = |p: &[T], q: &[T], r: &[T]| -> bool {
488 q[0] <= p[0].max(r[0])
489 && q[0] >= p[0].min(r[0])
490 && q[1] <= p[1].max(r[1])
491 && q[1] >= p[1].min(r[1])
492 };
493
494 let o1 = orientation(a1, a2, b1);
495 let o2 = orientation(a1, a2, b2);
496 let o3 = orientation(b1, b2, a1);
497 let o4 = orientation(b1, b2, a2);
498
499 if o1 != o2 && o3 != o4 {
501 return true;
502 }
503
504 if o1 == 0 && on_segment(a1, b1, a2) {
506 return true;
507 }
508
509 if o2 == 0 && on_segment(a1, b2, a2) {
510 return true;
511 }
512
513 if o3 == 0 && on_segment(b1, a1, b2) {
514 return true;
515 }
516
517 if o4 == 0 && on_segment(b1, a2, b2) {
518 return true;
519 }
520
521 false
522}
523
524#[allow(dead_code)]
536fn segments_overlap<T: Float>(a1: &[T], a2: &[T], b1: &[T], b2: &[T], epsilon: T) -> bool {
537 let cross = (a2[0] - a1[0]) * (b2[1] - b1[1]) - (a2[1] - a1[1]) * (b2[0] - b1[0]);
539
540 if cross.abs() > epsilon {
541 return false; }
543
544 let overlap_x = !(a2[0] < b1[0].min(b2[0]) - epsilon || a1[0] > b1[0].max(b2[0]) + epsilon);
546
547 let overlap_y = !(a2[1] < b1[1].min(b2[1]) - epsilon || a1[1] > b1[1].max(b2[1]) + epsilon);
549
550 overlap_x && overlap_y
551}
552
553#[allow(dead_code)]
589pub fn is_simple_polygon<T: Float>(polygon: &ArrayView2<T>) -> bool {
590 let n = polygon.shape()[0];
591
592 if n < 3 {
593 return true; }
595
596 for i in 0..n {
598 let i1 = (i + 1) % n;
599
600 let a1 = [polygon[[i, 0]], polygon[[i, 1]]];
601 let a2 = [polygon[[i1, 0]], polygon[[i1, 1]]];
602
603 for j in i + 2..i + n - 1 {
604 let j_mod = j % n;
605 let j1 = (j_mod + 1) % n;
606
607 if j1 == i || j_mod == i1 {
609 continue;
610 }
611
612 let b1 = [polygon[[j_mod, 0]], polygon[[j_mod, 1]]];
613 let b2 = [polygon[[j1, 0]], polygon[[j1, 1]]];
614
615 if segments_intersect(&a1, &a2, &b1, &b2) {
616 return false; }
618 }
619 }
620
621 true
622}
623
624#[allow(dead_code)]
655pub fn convex_hull_graham<T: Float + std::fmt::Debug>(points: &ArrayView2<T>) -> Array2<T> {
656 let n = points.shape()[0];
657
658 if n <= 3 {
659 return points.to_owned();
661 }
662
663 let mut lowest = 0;
665 for i in 1..n {
666 if points[[i, 1]] < points[[lowest, 1]]
667 || (points[[i, 1]] == points[[lowest, 1]] && points[[i, 0]] < points[[lowest, 0]])
668 {
669 lowest = i;
670 }
671 }
672
673 let pivot_x = points[[lowest, 0]];
675 let pivot_y = points[[lowest, 1]];
676
677 let polar_angle = |x: T, y: T| -> T {
679 let dx = x - pivot_x;
680 let dy = y - pivot_y;
681
682 if dx.is_zero() && dy.is_zero() {
684 return T::neg_infinity();
685 }
686
687 dy.atan2(dx)
689 };
690
691 let mut indexedpoints: Vec<(usize, T)> = (0..n)
693 .map(|i| (i, polar_angle(points[[i, 0]], points[[i, 1]])))
694 .collect();
695
696 indexedpoints.sort_by(|a, b| {
697 let angle_cmp = a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal);
699
700 if angle_cmp == std::cmp::Ordering::Equal {
701 let dist_a =
703 (points[[a.0, 0]] - pivot_x).powi(2) + (points[[a.0, 1]] - pivot_y).powi(2);
704 let dist_b =
705 (points[[b.0, 0]] - pivot_x).powi(2) + (points[[b.0, 1]] - pivot_y).powi(2);
706 dist_a
707 .partial_cmp(&dist_b)
708 .unwrap_or(std::cmp::Ordering::Equal)
709 } else {
710 angle_cmp
711 }
712 });
713
714 let ccw = |i1: usize, i2: usize, i3: usize| -> bool {
716 let p1_x = points[[i1, 0]];
717 let p1_y = points[[i1, 1]];
718 let p2_x = points[[i2, 0]];
719 let p2_y = points[[i2, 1]];
720 let p3_x = points[[i3, 0]];
721 let p3_y = points[[i3, 1]];
722
723 let val = (p2_x - p1_x) * (p3_y - p1_y) - (p2_y - p1_y) * (p3_x - p1_x);
724 val > T::zero()
725 };
726
727 let mut hull_indices = Vec::new();
729
730 hull_indices.push(lowest);
732
733 for &(index_, _) in &indexedpoints {
734 if index_ == lowest {
736 continue;
737 }
738
739 while hull_indices.len() >= 2 {
740 let top = hull_indices.len() - 1;
741 if ccw(hull_indices[top - 1], hull_indices[top], index_) {
742 break;
743 }
744 hull_indices.pop();
745 }
746
747 hull_indices.push(index_);
748 }
749
750 let mut hull = Array2::zeros((hull_indices.len(), 2));
752 for (i, &idx) in hull_indices.iter().enumerate() {
753 hull[[i, 0]] = points[[idx, 0]];
754 hull[[i, 1]] = points[[idx, 1]];
755 }
756
757 hull
758}
759
760#[allow(dead_code)]
797pub fn douglas_peucker_simplify<T: Float + std::fmt::Debug>(
798 polygon: &ArrayView2<T>,
799 tolerance: T,
800) -> Array2<T> {
801 let n = polygon.shape()[0];
802
803 if n <= 2 {
804 return polygon.to_owned();
805 }
806
807 let mut keep = vec![false; n];
809
810 keep[0] = true;
812 keep[n - 1] = true;
813
814 douglas_peucker_recursive(polygon, 0, n - 1, tolerance, &mut keep);
816
817 let num_keep = keep.iter().filter(|&&x| x).count();
819
820 let mut simplified = Array2::zeros((num_keep, 2));
822 let mut simplified_idx = 0;
823
824 for i in 0..n {
825 if keep[i] {
826 simplified[[simplified_idx, 0]] = polygon[[i, 0]];
827 simplified[[simplified_idx, 1]] = polygon[[i, 1]];
828 simplified_idx += 1;
829 }
830 }
831
832 simplified
833}
834
835#[allow(dead_code)]
837fn douglas_peucker_recursive<T: Float>(
838 polygon: &ArrayView2<T>,
839 start: usize,
840 end: usize,
841 tolerance: T,
842 keep: &mut [bool],
843) {
844 if end <= start + 1 {
845 return;
846 }
847
848 let mut max_dist = T::zero();
850 let mut max_idx = start;
851
852 let startpoint = [polygon[[start, 0]], polygon[[start, 1]]];
853 let endpoint = [polygon[[end, 0]], polygon[[end, 1]]];
854
855 for i in start + 1..end {
856 let point = [polygon[[i, 0]], polygon[[i, 1]]];
857 let dist = perpendicular_distance(&point, &startpoint, &endpoint);
858
859 if dist > max_dist {
860 max_dist = dist;
861 max_idx = i;
862 }
863 }
864
865 if max_dist > tolerance {
867 keep[max_idx] = true;
868 douglas_peucker_recursive(polygon, start, max_idx, tolerance, keep);
869 douglas_peucker_recursive(polygon, max_idx, end, tolerance, keep);
870 }
871}
872
873#[allow(dead_code)]
875fn perpendicular_distance<T: Float>(point: &[T; 2], line_start: &[T; 2], lineend: &[T; 2]) -> T {
876 let dx = lineend[0] - line_start[0];
877 let dy = lineend[1] - line_start[1];
878
879 if dx.is_zero() && dy.is_zero() {
881 let px = point[0] - line_start[0];
882 let py = point[1] - line_start[1];
883 return (px * px + py * py).sqrt();
884 }
885
886 let numerator = ((dy * (point[0] - line_start[0])) - (dx * (point[1] - line_start[1]))).abs();
888 let denominator = (dx * dx + dy * dy).sqrt();
889
890 numerator / denominator
891}
892
893#[allow(dead_code)]
931pub fn visvalingam_whyatt_simplify<T: Float + std::fmt::Debug>(
932 polygon: &ArrayView2<T>,
933 min_area: T,
934) -> Array2<T> {
935 let n = polygon.shape()[0];
936
937 if n <= 3 {
938 return polygon.to_owned();
939 }
940
941 let mut vertices: Vec<(usize, T)> = Vec::new();
943 let mut active = vec![true; n];
944
945 for i in 0..n {
947 let area = calculate_triangle_area(polygon, i, &active);
948 vertices.push((i, area));
949 }
950
951 vertices.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
953
954 let removal_candidates: Vec<usize> = vertices
956 .iter()
957 .filter(|(_, area)| *area < min_area)
958 .map(|(idx_, _)| *idx_)
959 .collect();
960
961 for vertex_idx in removal_candidates {
962 if count_active(&active) > 3 && active[vertex_idx] {
963 active[vertex_idx] = false;
964
965 let prev = find_previous_active(vertex_idx, &active, n);
967 let next = find_next_active(vertex_idx, &active, n);
968
969 if let (Some(prev_idx), Some(next_idx)) = (prev, next) {
970 update_vertex_area(polygon, prev_idx, &active, &mut vertices);
972 update_vertex_area(polygon, next_idx, &active, &mut vertices);
974 }
975 }
976 }
977
978 let num_active = count_active(&active);
980 let mut simplified = Array2::zeros((num_active, 2));
981 let mut simplified_idx = 0;
982
983 for i in 0..n {
984 if active[i] {
985 simplified[[simplified_idx, 0]] = polygon[[i, 0]];
986 simplified[[simplified_idx, 1]] = polygon[[i, 1]];
987 simplified_idx += 1;
988 }
989 }
990
991 simplified
992}
993
994#[allow(dead_code)]
996fn calculate_triangle_area<T: Float>(
997 polygon: &ArrayView2<T>,
998 vertex_idx: usize,
999 active: &[bool],
1000) -> T {
1001 let n = polygon.shape()[0];
1002
1003 let prev = find_previous_active(vertex_idx, active, n);
1004 let next = find_next_active(vertex_idx, active, n);
1005
1006 match (prev, next) {
1007 (Some(prev_idx), Some(next_idx)) => {
1008 let p1 = [polygon[[prev_idx, 0]], polygon[[prev_idx, 1]]];
1009 let p2 = [polygon[[vertex_idx, 0]], polygon[[vertex_idx, 1]]];
1010 let p3 = [polygon[[next_idx, 0]], polygon[[next_idx, 1]]];
1011
1012 triangle_area(&p1, &p2, &p3)
1013 }
1014 _ => T::infinity(), }
1016}
1017
1018#[allow(dead_code)]
1020fn triangle_area<T: Float>(p1: &[T; 2], p2: &[T; 2], p3: &[T; 2]) -> T {
1021 ((p1[0] * (p2[1] - p3[1]) + p2[0] * (p3[1] - p1[1]) + p3[0] * (p1[1] - p2[1]))
1022 / (T::one() + T::one()))
1023 .abs()
1024}
1025
1026#[allow(dead_code)]
1028fn find_previous_active(current: usize, active: &[bool], n: usize) -> Option<usize> {
1029 for i in 1..n {
1030 let idx = (current + n - i) % n;
1031 if active[idx] && idx != current {
1032 return Some(idx);
1033 }
1034 }
1035 None
1036}
1037
1038#[allow(dead_code)]
1040fn find_next_active(current: usize, active: &[bool], n: usize) -> Option<usize> {
1041 for i in 1..n {
1042 let idx = (current + i) % n;
1043 if active[idx] && idx != current {
1044 return Some(idx);
1045 }
1046 }
1047 None
1048}
1049
1050#[allow(dead_code)]
1052fn count_active(active: &[bool]) -> usize {
1053 active.iter().filter(|&&x| x).count()
1054}
1055
1056#[allow(dead_code)]
1058fn update_vertex_area<T: Float + std::fmt::Debug>(
1059 polygon: &ArrayView2<T>,
1060 vertex_idx: usize,
1061 active: &[bool],
1062 vertices: &mut [(usize, T)],
1063) {
1064 let new_area = calculate_triangle_area(polygon, vertex_idx, active);
1065
1066 for (_idx, area) in vertices.iter_mut() {
1068 if *_idx == vertex_idx {
1069 *area = new_area;
1070 break;
1071 }
1072 }
1073}
1074
1075#[cfg(test)]
1076mod tests {
1077 use super::*;
1078 use approx::assert_relative_eq;
1079 use ndarray::array;
1080
1081 #[test]
1082 fn testpoint_in_polygon() {
1083 let square = array![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0],];
1085
1086 assert!(point_in_polygon(&[0.5, 0.5], &square.view()));
1088 assert!(point_in_polygon(&[0.1, 0.1], &square.view()));
1089 assert!(point_in_polygon(&[0.9, 0.9], &square.view()));
1090
1091 assert!(!point_in_polygon(&[1.5, 0.5], &square.view()));
1093 assert!(!point_in_polygon(&[-0.5, 0.5], &square.view()));
1094 assert!(!point_in_polygon(&[0.5, 1.5], &square.view()));
1095 assert!(!point_in_polygon(&[0.5, -0.5], &square.view()));
1096
1097 assert!(point_in_polygon(&[0.0, 0.5], &square.view()));
1099 assert!(point_in_polygon(&[1.0, 0.5], &square.view()));
1100 assert!(point_in_polygon(&[0.5, 0.0], &square.view()));
1101 assert!(point_in_polygon(&[0.5, 1.0], &square.view()));
1102
1103 let concave = array![[0.0, 0.0], [2.0, 0.0], [2.0, 2.0], [1.0, 1.0], [0.0, 2.0],];
1105
1106 assert!(!point_in_polygon(&[1.0, 1.5], &concave.view()));
1109
1110 assert!(point_in_polygon(&[0.5, 0.5], &concave.view()));
1112
1113 assert!(point_in_polygon(&[1.5, 0.5], &concave.view()));
1116 }
1117
1118 #[test]
1119 fn testpoint_on_boundary() {
1120 let square = array![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0],];
1122
1123 let epsilon = 1e-10;
1124
1125 assert!(point_on_boundary(&[0.0, 0.5], &square.view(), epsilon));
1127 assert!(point_on_boundary(&[1.0, 0.5], &square.view(), epsilon));
1128 assert!(point_on_boundary(&[0.5, 0.0], &square.view(), epsilon));
1129 assert!(point_on_boundary(&[0.5, 1.0], &square.view(), epsilon));
1130
1131 assert!(point_on_boundary(&[0.0, 0.0], &square.view(), epsilon));
1133 assert!(point_on_boundary(&[1.0, 0.0], &square.view(), epsilon));
1134 assert!(point_on_boundary(&[1.0, 1.0], &square.view(), epsilon));
1135 assert!(point_on_boundary(&[0.0, 1.0], &square.view(), epsilon));
1136
1137 assert!(!point_on_boundary(&[0.5, 0.5], &square.view(), epsilon));
1139 assert!(!point_on_boundary(&[1.5, 0.5], &square.view(), epsilon));
1140 assert!(!point_on_boundary(&[0.0, 2.0], &square.view(), epsilon));
1141
1142 assert!(point_on_boundary(&[0.0, 0.5 + 1e-5], &square.view(), 1e-4));
1145 }
1146
1147 #[test]
1148 fn testpolygon_area() {
1149 let square = array![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0],];
1151
1152 assert_relative_eq!(polygon_area(&square.view()), 1.0, epsilon = 1e-10);
1153
1154 let rectangle = array![[0.0, 0.0], [3.0, 0.0], [3.0, 2.0], [0.0, 2.0],];
1156
1157 assert_relative_eq!(polygon_area(&rectangle.view()), 6.0, epsilon = 1e-10);
1158
1159 let triangle = array![[0.0, 0.0], [1.0, 0.0], [0.0, 1.0],];
1161
1162 assert_relative_eq!(polygon_area(&triangle.view()), 0.5, epsilon = 1e-10);
1163
1164 let lshape = array![
1166 [0.0, 0.0],
1167 [2.0, 0.0],
1168 [2.0, 1.0],
1169 [1.0, 1.0],
1170 [1.0, 2.0],
1171 [0.0, 2.0],
1172 ];
1173
1174 assert_relative_eq!(polygon_area(&lshape.view()), 3.0, epsilon = 1e-10);
1175 }
1176
1177 #[test]
1178 fn testpolygon_centroid() {
1179 let square = array![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0],];
1181
1182 let centroid = polygon_centroid(&square.view());
1183 assert_relative_eq!(centroid[0], 0.5, epsilon = 1e-10);
1184 assert_relative_eq!(centroid[1], 0.5, epsilon = 1e-10);
1185
1186 let rectangle = array![[0.0, 0.0], [3.0, 0.0], [3.0, 2.0], [0.0, 2.0],];
1188
1189 let centroid = polygon_centroid(&rectangle.view());
1190 assert_relative_eq!(centroid[0], 1.5, epsilon = 1e-10);
1191 assert_relative_eq!(centroid[1], 1.0, epsilon = 1e-10);
1192
1193 let triangle = array![[0.0, 0.0], [1.0, 0.0], [0.0, 1.0],];
1195
1196 let centroid = polygon_centroid(&triangle.view());
1197 assert_relative_eq!(centroid[0], 1.0 / 3.0, epsilon = 1e-10);
1198 assert_relative_eq!(centroid[1], 1.0 / 3.0, epsilon = 1e-10);
1199 }
1200
1201 #[test]
1202 fn testpolygon_contains_polygon() {
1203 let outer = array![[0.0, 0.0], [3.0, 0.0], [3.0, 3.0], [0.0, 3.0],];
1205
1206 let inner = array![[1.0, 1.0], [2.0, 1.0], [2.0, 2.0], [1.0, 2.0],];
1208
1209 assert!(polygon_contains_polygon(&outer.view(), &inner.view()));
1211
1212 assert!(!polygon_contains_polygon(&inner.view(), &outer.view()));
1214
1215 let overlap = array![[2.0, 2.0], [4.0, 2.0], [4.0, 4.0], [2.0, 4.0],];
1217
1218 assert!(!polygon_contains_polygon(&outer.view(), &overlap.view()));
1220 assert!(!polygon_contains_polygon(&overlap.view(), &outer.view()));
1221
1222 assert!(polygon_contains_polygon(&outer.view(), &outer.view()));
1224 }
1225
1226 #[test]
1227 fn test_is_simple_polygon() {
1228 let square = array![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0],];
1230
1231 assert!(is_simple_polygon(&square.view()));
1232
1233 let bowtie = array![[0.0, 0.0], [1.0, 1.0], [0.0, 1.0], [1.0, 0.0],];
1235
1236 assert!(!is_simple_polygon(&bowtie.view()));
1237
1238 let complex = array![[0.0, 0.0], [2.0, 0.0], [2.0, 2.0], [1.0, 1.0], [0.0, 2.0],];
1240
1241 assert!(is_simple_polygon(&complex.view()));
1242 }
1243
1244 #[test]
1245 fn test_convex_hull_graham() {
1246 let points = array![
1248 [0.0, 0.0],
1249 [1.0, 0.0],
1250 [0.5, 0.5], [1.0, 1.0],
1252 [0.0, 1.0],
1253 ];
1254
1255 let hull = convex_hull_graham(&points.view());
1256
1257 assert_eq!(hull.shape()[0], 4);
1259
1260 let mut found_inside = false;
1262 for i in 0..hull.shape()[0] {
1263 if (hull[[i, 0]] - 0.5).abs() < 1e-10 && (hull[[i, 1]] - 0.5).abs() < 1e-10 {
1264 found_inside = true;
1265 break;
1266 }
1267 }
1268 assert!(!found_inside);
1269
1270 let mut circlepoints = Array2::zeros((8, 2));
1272 for i in 0..8 {
1273 let angle = 2.0 * std::f64::consts::PI * (i as f64) / 8.0;
1274 circlepoints[[i, 0]] = angle.cos();
1275 circlepoints[[i, 1]] = angle.sin();
1276 }
1277
1278 let allpoints = array![
1280 [0.0, 0.0], [0.5, 0.0], [0.0, 0.5], [-0.5, 0.0], [0.0, -0.5], [1.0, 0.0],
1287 [
1288 std::f64::consts::FRAC_1_SQRT_2,
1289 std::f64::consts::FRAC_1_SQRT_2
1290 ],
1291 [0.0, 1.0],
1292 [
1293 -std::f64::consts::FRAC_1_SQRT_2,
1294 std::f64::consts::FRAC_1_SQRT_2
1295 ],
1296 [-1.0, 0.0],
1297 [
1298 -std::f64::consts::FRAC_1_SQRT_2,
1299 -std::f64::consts::FRAC_1_SQRT_2
1300 ],
1301 [0.0, -1.0],
1302 [
1303 std::f64::consts::FRAC_1_SQRT_2,
1304 -std::f64::consts::FRAC_1_SQRT_2
1305 ],
1306 ];
1307
1308 let hull = convex_hull_graham(&allpoints.view());
1309
1310 assert_eq!(hull.shape()[0], 8);
1312 }
1313}