1use scirs2_core::ndarray::{Array2, ArrayView2};
32use scirs2_core::numeric::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).expect("Operation failed");
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![
309 x_sum / T::from(n).expect("Operation failed"),
310 y_sum / T::from(n).expect("Operation failed"),
311 ];
312 } else {
313 return vec![T::zero(), T::zero()];
314 }
315 }
316
317 let mut cx = T::zero();
318 let mut cy = T::zero();
319 let mut area = T::zero();
320
321 for i in 0..n {
322 let j = (i + 1) % n;
323
324 let xi = polygon[[i, 0]];
325 let yi = polygon[[i, 1]];
326 let xj = polygon[[j, 0]];
327 let yj = polygon[[j, 1]];
328
329 let cross = xi * yj - xj * yi;
330
331 cx = cx + (xi + xj) * cross;
332 cy = cy + (yi + yj) * cross;
333 area = area + cross;
334 }
335
336 area = area / (T::one() + T::one());
337
338 if area.is_zero() {
339 let mut x_sum = T::zero();
341 let mut y_sum = T::zero();
342
343 for i in 0..n {
344 x_sum = x_sum + polygon[[i, 0]];
345 y_sum = y_sum + polygon[[i, 1]];
346 }
347
348 return vec![
349 x_sum / T::from(n).expect("Operation failed"),
350 y_sum / T::from(n).expect("Operation failed"),
351 ];
352 }
353
354 let six = T::from(6).expect("Operation failed");
355 cx = cx / (six * area);
356 cy = cy / (six * area);
357
358 vec![cx.abs(), cy.abs()]
361}
362
363#[allow(dead_code)]
400pub fn polygon_contains_polygon<T: Float>(
401 polygon_a: &ArrayView2<T>,
402 polygon_b: &ArrayView2<T>,
403) -> bool {
404 if polygon_a.shape() == polygon_b.shape() {
406 let n = polygon_a.shape()[0];
407 let mut same = true;
408
409 for i in 0..n {
410 if polygon_a[[i, 0]] != polygon_b[[i, 0]] || polygon_a[[i, 1]] != polygon_b[[i, 1]] {
411 same = false;
412 break;
413 }
414 }
415
416 if same {
417 return true;
418 }
419 }
420
421 let n_b = polygon_b.shape()[0];
422
423 for i in 0..n_b {
425 let point = [polygon_b[[i, 0]], polygon_b[[i, 1]]];
426 if !point_in_polygon(&point, polygon_a) {
427 return false;
428 }
429 }
430
431 if polygon_a.shape() != polygon_b.shape() {
433 let n_a = polygon_a.shape()[0];
434
435 for i in 0..n_a {
437 let j = (i + 1) % n_a;
438 let a1 = [polygon_a[[i, 0]], polygon_a[[i, 1]]];
439 let a2 = [polygon_a[[j, 0]], polygon_a[[j, 1]]];
440
441 for k in 0..n_b {
442 let l = (k + 1) % n_b;
443 let b1 = [polygon_b[[k, 0]], polygon_b[[k, 1]]];
444 let b2 = [polygon_b[[l, 0]], polygon_b[[l, 1]]];
445
446 if segments_intersect(&a1, &a2, &b1, &b2)
448 && !segments_overlap(&a1, &a2, &b1, &b2, T::epsilon())
449 && !point_on_boundary(&a1, polygon_b, T::epsilon())
450 && !point_on_boundary(&a2, polygon_b, T::epsilon())
451 && !point_on_boundary(&b1, polygon_a, T::epsilon())
452 && !point_on_boundary(&b2, polygon_a, T::epsilon())
453 {
454 return false;
455 }
456 }
457 }
458 }
459
460 true
461}
462
463#[allow(dead_code)]
474fn segments_intersect<T: Float>(a1: &[T], a2: &[T], b1: &[T], b2: &[T]) -> bool {
475 let orientation = |p: &[T], q: &[T], r: &[T]| -> i32 {
481 let val = (q[1] - p[1]) * (r[0] - q[0]) - (q[0] - p[0]) * (r[1] - q[1]);
482
483 if val < T::zero() {
484 return 1; } else if val > T::zero() {
486 return 2; }
488
489 0 };
491
492 let on_segment = |p: &[T], q: &[T], r: &[T]| -> bool {
494 q[0] <= p[0].max(r[0])
495 && q[0] >= p[0].min(r[0])
496 && q[1] <= p[1].max(r[1])
497 && q[1] >= p[1].min(r[1])
498 };
499
500 let o1 = orientation(a1, a2, b1);
501 let o2 = orientation(a1, a2, b2);
502 let o3 = orientation(b1, b2, a1);
503 let o4 = orientation(b1, b2, a2);
504
505 if o1 != o2 && o3 != o4 {
507 return true;
508 }
509
510 if o1 == 0 && on_segment(a1, b1, a2) {
512 return true;
513 }
514
515 if o2 == 0 && on_segment(a1, b2, a2) {
516 return true;
517 }
518
519 if o3 == 0 && on_segment(b1, a1, b2) {
520 return true;
521 }
522
523 if o4 == 0 && on_segment(b1, a2, b2) {
524 return true;
525 }
526
527 false
528}
529
530#[allow(dead_code)]
542fn segments_overlap<T: Float>(a1: &[T], a2: &[T], b1: &[T], b2: &[T], epsilon: T) -> bool {
543 let cross = (a2[0] - a1[0]) * (b2[1] - b1[1]) - (a2[1] - a1[1]) * (b2[0] - b1[0]);
545
546 if cross.abs() > epsilon {
547 return false; }
549
550 let overlap_x = !(a2[0] < b1[0].min(b2[0]) - epsilon || a1[0] > b1[0].max(b2[0]) + epsilon);
552
553 let overlap_y = !(a2[1] < b1[1].min(b2[1]) - epsilon || a1[1] > b1[1].max(b2[1]) + epsilon);
555
556 overlap_x && overlap_y
557}
558
559#[allow(dead_code)]
595pub fn is_simple_polygon<T: Float>(polygon: &ArrayView2<T>) -> bool {
596 let n = polygon.shape()[0];
597
598 if n < 3 {
599 return true; }
601
602 for i in 0..n {
604 let i1 = (i + 1) % n;
605
606 let a1 = [polygon[[i, 0]], polygon[[i, 1]]];
607 let a2 = [polygon[[i1, 0]], polygon[[i1, 1]]];
608
609 for j in i + 2..i + n - 1 {
610 let j_mod = j % n;
611 let j1 = (j_mod + 1) % n;
612
613 if j1 == i || j_mod == i1 {
615 continue;
616 }
617
618 let b1 = [polygon[[j_mod, 0]], polygon[[j_mod, 1]]];
619 let b2 = [polygon[[j1, 0]], polygon[[j1, 1]]];
620
621 if segments_intersect(&a1, &a2, &b1, &b2) {
622 return false; }
624 }
625 }
626
627 true
628}
629
630#[allow(dead_code)]
661pub fn convex_hull_graham<T: Float + std::fmt::Debug>(points: &ArrayView2<T>) -> Array2<T> {
662 let n = points.shape()[0];
663
664 if n <= 3 {
665 return points.to_owned();
667 }
668
669 let mut lowest = 0;
671 for i in 1..n {
672 if points[[i, 1]] < points[[lowest, 1]]
673 || (points[[i, 1]] == points[[lowest, 1]] && points[[i, 0]] < points[[lowest, 0]])
674 {
675 lowest = i;
676 }
677 }
678
679 let pivot_x = points[[lowest, 0]];
681 let pivot_y = points[[lowest, 1]];
682
683 let polar_angle = |x: T, y: T| -> T {
685 let dx = x - pivot_x;
686 let dy = y - pivot_y;
687
688 if dx.is_zero() && dy.is_zero() {
690 return T::neg_infinity();
691 }
692
693 dy.atan2(dx)
695 };
696
697 let mut indexedpoints: Vec<(usize, T)> = (0..n)
699 .map(|i| (i, polar_angle(points[[i, 0]], points[[i, 1]])))
700 .collect();
701
702 indexedpoints.sort_by(|a, b| {
703 let angle_cmp = a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal);
705
706 if angle_cmp == std::cmp::Ordering::Equal {
707 let dist_a =
709 (points[[a.0, 0]] - pivot_x).powi(2) + (points[[a.0, 1]] - pivot_y).powi(2);
710 let dist_b =
711 (points[[b.0, 0]] - pivot_x).powi(2) + (points[[b.0, 1]] - pivot_y).powi(2);
712 dist_a
713 .partial_cmp(&dist_b)
714 .unwrap_or(std::cmp::Ordering::Equal)
715 } else {
716 angle_cmp
717 }
718 });
719
720 let ccw = |i1: usize, i2: usize, i3: usize| -> bool {
722 let p1_x = points[[i1, 0]];
723 let p1_y = points[[i1, 1]];
724 let p2_x = points[[i2, 0]];
725 let p2_y = points[[i2, 1]];
726 let p3_x = points[[i3, 0]];
727 let p3_y = points[[i3, 1]];
728
729 let val = (p2_x - p1_x) * (p3_y - p1_y) - (p2_y - p1_y) * (p3_x - p1_x);
730 val > T::zero()
731 };
732
733 let mut hull_indices = Vec::new();
735
736 hull_indices.push(lowest);
738
739 for &(index_, _) in &indexedpoints {
740 if index_ == lowest {
742 continue;
743 }
744
745 while hull_indices.len() >= 2 {
746 let top = hull_indices.len() - 1;
747 if ccw(hull_indices[top - 1], hull_indices[top], index_) {
748 break;
749 }
750 hull_indices.pop();
751 }
752
753 hull_indices.push(index_);
754 }
755
756 let mut hull = Array2::zeros((hull_indices.len(), 2));
758 for (i, &idx) in hull_indices.iter().enumerate() {
759 hull[[i, 0]] = points[[idx, 0]];
760 hull[[i, 1]] = points[[idx, 1]];
761 }
762
763 hull
764}
765
766#[allow(dead_code)]
803pub fn douglas_peucker_simplify<T: Float + std::fmt::Debug>(
804 polygon: &ArrayView2<T>,
805 tolerance: T,
806) -> Array2<T> {
807 let n = polygon.shape()[0];
808
809 if n <= 2 {
810 return polygon.to_owned();
811 }
812
813 let mut keep = vec![false; n];
815
816 keep[0] = true;
818 keep[n - 1] = true;
819
820 douglas_peucker_recursive(polygon, 0, n - 1, tolerance, &mut keep);
822
823 let num_keep = keep.iter().filter(|&&x| x).count();
825
826 let mut simplified = Array2::zeros((num_keep, 2));
828 let mut simplified_idx = 0;
829
830 for i in 0..n {
831 if keep[i] {
832 simplified[[simplified_idx, 0]] = polygon[[i, 0]];
833 simplified[[simplified_idx, 1]] = polygon[[i, 1]];
834 simplified_idx += 1;
835 }
836 }
837
838 simplified
839}
840
841#[allow(dead_code)]
843fn douglas_peucker_recursive<T: Float>(
844 polygon: &ArrayView2<T>,
845 start: usize,
846 end: usize,
847 tolerance: T,
848 keep: &mut [bool],
849) {
850 if end <= start + 1 {
851 return;
852 }
853
854 let mut max_dist = T::zero();
856 let mut max_idx = start;
857
858 let startpoint = [polygon[[start, 0]], polygon[[start, 1]]];
859 let endpoint = [polygon[[end, 0]], polygon[[end, 1]]];
860
861 for i in start + 1..end {
862 let point = [polygon[[i, 0]], polygon[[i, 1]]];
863 let dist = perpendicular_distance(&point, &startpoint, &endpoint);
864
865 if dist > max_dist {
866 max_dist = dist;
867 max_idx = i;
868 }
869 }
870
871 if max_dist > tolerance {
873 keep[max_idx] = true;
874 douglas_peucker_recursive(polygon, start, max_idx, tolerance, keep);
875 douglas_peucker_recursive(polygon, max_idx, end, tolerance, keep);
876 }
877}
878
879#[allow(dead_code)]
881fn perpendicular_distance<T: Float>(point: &[T; 2], line_start: &[T; 2], lineend: &[T; 2]) -> T {
882 let dx = lineend[0] - line_start[0];
883 let dy = lineend[1] - line_start[1];
884
885 if dx.is_zero() && dy.is_zero() {
887 let px = point[0] - line_start[0];
888 let py = point[1] - line_start[1];
889 return (px * px + py * py).sqrt();
890 }
891
892 let numerator = ((dy * (point[0] - line_start[0])) - (dx * (point[1] - line_start[1]))).abs();
894 let denominator = (dx * dx + dy * dy).sqrt();
895
896 numerator / denominator
897}
898
899#[allow(dead_code)]
937pub fn visvalingam_whyatt_simplify<T: Float + std::fmt::Debug>(
938 polygon: &ArrayView2<T>,
939 min_area: T,
940) -> Array2<T> {
941 let n = polygon.shape()[0];
942
943 if n <= 3 {
944 return polygon.to_owned();
945 }
946
947 let mut vertices: Vec<(usize, T)> = Vec::new();
949 let mut active = vec![true; n];
950
951 for i in 0..n {
953 let area = calculate_triangle_area(polygon, i, &active);
954 vertices.push((i, area));
955 }
956
957 vertices.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
959
960 let removal_candidates: Vec<usize> = vertices
962 .iter()
963 .filter(|(_, area)| *area < min_area)
964 .map(|(idx_, _)| *idx_)
965 .collect();
966
967 for vertex_idx in removal_candidates {
968 if count_active(&active) > 3 && active[vertex_idx] {
969 active[vertex_idx] = false;
970
971 let prev = find_previous_active(vertex_idx, &active, n);
973 let next = find_next_active(vertex_idx, &active, n);
974
975 if let (Some(prev_idx), Some(next_idx)) = (prev, next) {
976 update_vertex_area(polygon, prev_idx, &active, &mut vertices);
978 update_vertex_area(polygon, next_idx, &active, &mut vertices);
980 }
981 }
982 }
983
984 let num_active = count_active(&active);
986 let mut simplified = Array2::zeros((num_active, 2));
987 let mut simplified_idx = 0;
988
989 for i in 0..n {
990 if active[i] {
991 simplified[[simplified_idx, 0]] = polygon[[i, 0]];
992 simplified[[simplified_idx, 1]] = polygon[[i, 1]];
993 simplified_idx += 1;
994 }
995 }
996
997 simplified
998}
999
1000#[allow(dead_code)]
1002fn calculate_triangle_area<T: Float>(
1003 polygon: &ArrayView2<T>,
1004 vertex_idx: usize,
1005 active: &[bool],
1006) -> T {
1007 let n = polygon.shape()[0];
1008
1009 let prev = find_previous_active(vertex_idx, active, n);
1010 let next = find_next_active(vertex_idx, active, n);
1011
1012 match (prev, next) {
1013 (Some(prev_idx), Some(next_idx)) => {
1014 let p1 = [polygon[[prev_idx, 0]], polygon[[prev_idx, 1]]];
1015 let p2 = [polygon[[vertex_idx, 0]], polygon[[vertex_idx, 1]]];
1016 let p3 = [polygon[[next_idx, 0]], polygon[[next_idx, 1]]];
1017
1018 triangle_area(&p1, &p2, &p3)
1019 }
1020 _ => T::infinity(), }
1022}
1023
1024#[allow(dead_code)]
1026fn triangle_area<T: Float>(p1: &[T; 2], p2: &[T; 2], p3: &[T; 2]) -> T {
1027 ((p1[0] * (p2[1] - p3[1]) + p2[0] * (p3[1] - p1[1]) + p3[0] * (p1[1] - p2[1]))
1028 / (T::one() + T::one()))
1029 .abs()
1030}
1031
1032#[allow(dead_code)]
1034fn find_previous_active(current: usize, active: &[bool], n: usize) -> Option<usize> {
1035 for i in 1..n {
1036 let idx = (current + n - i) % n;
1037 if active[idx] && idx != current {
1038 return Some(idx);
1039 }
1040 }
1041 None
1042}
1043
1044#[allow(dead_code)]
1046fn find_next_active(current: usize, active: &[bool], n: usize) -> Option<usize> {
1047 for i in 1..n {
1048 let idx = (current + i) % n;
1049 if active[idx] && idx != current {
1050 return Some(idx);
1051 }
1052 }
1053 None
1054}
1055
1056#[allow(dead_code)]
1058fn count_active(active: &[bool]) -> usize {
1059 active.iter().filter(|&&x| x).count()
1060}
1061
1062#[allow(dead_code)]
1064fn update_vertex_area<T: Float + std::fmt::Debug>(
1065 polygon: &ArrayView2<T>,
1066 vertex_idx: usize,
1067 active: &[bool],
1068 vertices: &mut [(usize, T)],
1069) {
1070 let new_area = calculate_triangle_area(polygon, vertex_idx, active);
1071
1072 for (_idx, area) in vertices.iter_mut() {
1074 if *_idx == vertex_idx {
1075 *area = new_area;
1076 break;
1077 }
1078 }
1079}
1080
1081#[cfg(test)]
1082mod tests {
1083 use super::*;
1084 use approx::assert_relative_eq;
1085 use scirs2_core::ndarray::array;
1086
1087 #[test]
1088 fn testpoint_in_polygon() {
1089 let square = array![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0],];
1091
1092 assert!(point_in_polygon(&[0.5, 0.5], &square.view()));
1094 assert!(point_in_polygon(&[0.1, 0.1], &square.view()));
1095 assert!(point_in_polygon(&[0.9, 0.9], &square.view()));
1096
1097 assert!(!point_in_polygon(&[1.5, 0.5], &square.view()));
1099 assert!(!point_in_polygon(&[-0.5, 0.5], &square.view()));
1100 assert!(!point_in_polygon(&[0.5, 1.5], &square.view()));
1101 assert!(!point_in_polygon(&[0.5, -0.5], &square.view()));
1102
1103 assert!(point_in_polygon(&[0.0, 0.5], &square.view()));
1105 assert!(point_in_polygon(&[1.0, 0.5], &square.view()));
1106 assert!(point_in_polygon(&[0.5, 0.0], &square.view()));
1107 assert!(point_in_polygon(&[0.5, 1.0], &square.view()));
1108
1109 let concave = array![[0.0, 0.0], [2.0, 0.0], [2.0, 2.0], [1.0, 1.0], [0.0, 2.0],];
1111
1112 assert!(!point_in_polygon(&[1.0, 1.5], &concave.view()));
1115
1116 assert!(point_in_polygon(&[0.5, 0.5], &concave.view()));
1118
1119 assert!(point_in_polygon(&[1.5, 0.5], &concave.view()));
1122 }
1123
1124 #[test]
1125 fn testpoint_on_boundary() {
1126 let square = array![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0],];
1128
1129 let epsilon = 1e-10;
1130
1131 assert!(point_on_boundary(&[0.0, 0.5], &square.view(), epsilon));
1133 assert!(point_on_boundary(&[1.0, 0.5], &square.view(), epsilon));
1134 assert!(point_on_boundary(&[0.5, 0.0], &square.view(), epsilon));
1135 assert!(point_on_boundary(&[0.5, 1.0], &square.view(), epsilon));
1136
1137 assert!(point_on_boundary(&[0.0, 0.0], &square.view(), epsilon));
1139 assert!(point_on_boundary(&[1.0, 0.0], &square.view(), epsilon));
1140 assert!(point_on_boundary(&[1.0, 1.0], &square.view(), epsilon));
1141 assert!(point_on_boundary(&[0.0, 1.0], &square.view(), epsilon));
1142
1143 assert!(!point_on_boundary(&[0.5, 0.5], &square.view(), epsilon));
1145 assert!(!point_on_boundary(&[1.5, 0.5], &square.view(), epsilon));
1146 assert!(!point_on_boundary(&[0.0, 2.0], &square.view(), epsilon));
1147
1148 assert!(point_on_boundary(&[0.0, 0.5 + 1e-5], &square.view(), 1e-4));
1151 }
1152
1153 #[test]
1154 fn testpolygon_area() {
1155 let square = array![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0],];
1157
1158 assert_relative_eq!(polygon_area(&square.view()), 1.0, epsilon = 1e-10);
1159
1160 let rectangle = array![[0.0, 0.0], [3.0, 0.0], [3.0, 2.0], [0.0, 2.0],];
1162
1163 assert_relative_eq!(polygon_area(&rectangle.view()), 6.0, epsilon = 1e-10);
1164
1165 let triangle = array![[0.0, 0.0], [1.0, 0.0], [0.0, 1.0],];
1167
1168 assert_relative_eq!(polygon_area(&triangle.view()), 0.5, epsilon = 1e-10);
1169
1170 let lshape = array![
1172 [0.0, 0.0],
1173 [2.0, 0.0],
1174 [2.0, 1.0],
1175 [1.0, 1.0],
1176 [1.0, 2.0],
1177 [0.0, 2.0],
1178 ];
1179
1180 assert_relative_eq!(polygon_area(&lshape.view()), 3.0, epsilon = 1e-10);
1181 }
1182
1183 #[test]
1184 fn testpolygon_centroid() {
1185 let square = array![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0],];
1187
1188 let centroid = polygon_centroid(&square.view());
1189 assert_relative_eq!(centroid[0], 0.5, epsilon = 1e-10);
1190 assert_relative_eq!(centroid[1], 0.5, epsilon = 1e-10);
1191
1192 let rectangle = array![[0.0, 0.0], [3.0, 0.0], [3.0, 2.0], [0.0, 2.0],];
1194
1195 let centroid = polygon_centroid(&rectangle.view());
1196 assert_relative_eq!(centroid[0], 1.5, epsilon = 1e-10);
1197 assert_relative_eq!(centroid[1], 1.0, epsilon = 1e-10);
1198
1199 let triangle = array![[0.0, 0.0], [1.0, 0.0], [0.0, 1.0],];
1201
1202 let centroid = polygon_centroid(&triangle.view());
1203 assert_relative_eq!(centroid[0], 1.0 / 3.0, epsilon = 1e-10);
1204 assert_relative_eq!(centroid[1], 1.0 / 3.0, epsilon = 1e-10);
1205 }
1206
1207 #[test]
1208 fn testpolygon_contains_polygon() {
1209 let outer = array![[0.0, 0.0], [3.0, 0.0], [3.0, 3.0], [0.0, 3.0],];
1211
1212 let inner = array![[1.0, 1.0], [2.0, 1.0], [2.0, 2.0], [1.0, 2.0],];
1214
1215 assert!(polygon_contains_polygon(&outer.view(), &inner.view()));
1217
1218 assert!(!polygon_contains_polygon(&inner.view(), &outer.view()));
1220
1221 let overlap = array![[2.0, 2.0], [4.0, 2.0], [4.0, 4.0], [2.0, 4.0],];
1223
1224 assert!(!polygon_contains_polygon(&outer.view(), &overlap.view()));
1226 assert!(!polygon_contains_polygon(&overlap.view(), &outer.view()));
1227
1228 assert!(polygon_contains_polygon(&outer.view(), &outer.view()));
1230 }
1231
1232 #[test]
1233 fn test_is_simple_polygon() {
1234 let square = array![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0],];
1236
1237 assert!(is_simple_polygon(&square.view()));
1238
1239 let bowtie = array![[0.0, 0.0], [1.0, 1.0], [0.0, 1.0], [1.0, 0.0],];
1241
1242 assert!(!is_simple_polygon(&bowtie.view()));
1243
1244 let complex = array![[0.0, 0.0], [2.0, 0.0], [2.0, 2.0], [1.0, 1.0], [0.0, 2.0],];
1246
1247 assert!(is_simple_polygon(&complex.view()));
1248 }
1249
1250 #[test]
1251 fn test_convex_hull_graham() {
1252 let points = array![
1254 [0.0, 0.0],
1255 [1.0, 0.0],
1256 [0.5, 0.5], [1.0, 1.0],
1258 [0.0, 1.0],
1259 ];
1260
1261 let hull = convex_hull_graham(&points.view());
1262
1263 assert_eq!(hull.shape()[0], 4);
1265
1266 let mut found_inside = false;
1268 for i in 0..hull.shape()[0] {
1269 if (hull[[i, 0]] - 0.5).abs() < 1e-10 && (hull[[i, 1]] - 0.5).abs() < 1e-10 {
1270 found_inside = true;
1271 break;
1272 }
1273 }
1274 assert!(!found_inside);
1275
1276 let mut circlepoints = Array2::zeros((8, 2));
1278 for i in 0..8 {
1279 let angle = 2.0 * std::f64::consts::PI * (i as f64) / 8.0;
1280 circlepoints[[i, 0]] = angle.cos();
1281 circlepoints[[i, 1]] = angle.sin();
1282 }
1283
1284 let allpoints = array![
1286 [0.0, 0.0], [0.5, 0.0], [0.0, 0.5], [-0.5, 0.0], [0.0, -0.5], [1.0, 0.0],
1293 [
1294 std::f64::consts::FRAC_1_SQRT_2,
1295 std::f64::consts::FRAC_1_SQRT_2
1296 ],
1297 [0.0, 1.0],
1298 [
1299 -std::f64::consts::FRAC_1_SQRT_2,
1300 std::f64::consts::FRAC_1_SQRT_2
1301 ],
1302 [-1.0, 0.0],
1303 [
1304 -std::f64::consts::FRAC_1_SQRT_2,
1305 -std::f64::consts::FRAC_1_SQRT_2
1306 ],
1307 [0.0, -1.0],
1308 [
1309 std::f64::consts::FRAC_1_SQRT_2,
1310 -std::f64::consts::FRAC_1_SQRT_2
1311 ],
1312 ];
1313
1314 let hull = convex_hull_graham(&allpoints.view());
1315
1316 assert_eq!(hull.shape()[0], 8);
1318 }
1319}