use scirs2_core::ndarray::{Array2, ArrayView2};
use scirs2_core::numeric::Float;
#[allow(dead_code)]
pub fn point_in_polygon<T: Float>(point: &[T], polygon: &ArrayView2<T>) -> bool {
let x = point[0];
let y = point[1];
let n = polygon.shape()[0];
if n < 3 {
return false; }
let epsilon = T::from(1e-10).expect("Operation failed");
if point_on_boundary(point, polygon, epsilon) {
return true;
}
let mut inside = false;
for i in 0..n {
let j = (i + 1) % n;
let vi_y = polygon[[i, 1]];
let vj_y = polygon[[j, 1]];
if ((vi_y <= y) && (vj_y > y)) || ((vi_y > y) && (vj_y <= y)) {
let vi_x = polygon[[i, 0]];
let vj_x = polygon[[j, 0]];
let slope = (vj_x - vi_x) / (vj_y - vi_y);
let intersect_x = vi_x + (y - vi_y) * slope;
if x < intersect_x {
inside = !inside;
}
}
}
inside
}
#[allow(dead_code)]
pub fn point_on_boundary<T: Float>(point: &[T], polygon: &ArrayView2<T>, epsilon: T) -> bool {
let x = point[0];
let y = point[1];
let n = polygon.shape()[0];
if n < 2 {
return false;
}
for i in 0..n {
let j = (i + 1) % n;
let x1 = polygon[[i, 0]];
let y1 = polygon[[i, 1]];
let x2 = polygon[[j, 0]];
let y2 = polygon[[j, 1]];
let length_squared = (x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1);
if length_squared.is_zero() {
let dist = ((x - x1) * (x - x1) + (y - y1) * (y - y1)).sqrt();
if dist < epsilon {
return true;
}
} else {
let t = ((x - x1) * (x2 - x1) + (y - y1) * (y2 - y1)) / length_squared;
if t < T::zero() {
let dist = ((x - x1) * (x - x1) + (y - y1) * (y - y1)).sqrt();
if dist < epsilon {
return true;
}
} else if t > T::one() {
let dist = ((x - x2) * (x - x2) + (y - y2) * (y - y2)).sqrt();
if dist < epsilon {
return true;
}
} else {
let projection_x = x1 + t * (x2 - x1);
let projection_y = y1 + t * (y2 - y1);
let dist = ((x - projection_x) * (x - projection_x)
+ (y - projection_y) * (y - projection_y))
.sqrt();
if dist < epsilon {
return true;
}
}
}
}
false
}
#[allow(dead_code)]
pub fn polygon_area<T: Float>(polygon: &ArrayView2<T>) -> T {
let n = polygon.shape()[0];
if n < 3 {
return T::zero(); }
let mut area = T::zero();
for i in 0..n {
let j = (i + 1) % n;
let xi = polygon[[i, 0]];
let yi = polygon[[i, 1]];
let xj = polygon[[j, 0]];
let yj = polygon[[j, 1]];
area = area + (xi * yj - xj * yi);
}
(area / (T::one() + T::one())).abs()
}
#[allow(dead_code)]
pub fn polygon_centroid<T: Float>(polygon: &ArrayView2<T>) -> Vec<T> {
let n = polygon.shape()[0];
if n < 3 {
let mut x_sum = T::zero();
let mut y_sum = T::zero();
for i in 0..n {
x_sum = x_sum + polygon[[i, 0]];
y_sum = y_sum + polygon[[i, 1]];
}
if n > 0 {
return vec![
x_sum / T::from(n).expect("Operation failed"),
y_sum / T::from(n).expect("Operation failed"),
];
} else {
return vec![T::zero(), T::zero()];
}
}
let mut cx = T::zero();
let mut cy = T::zero();
let mut area = T::zero();
for i in 0..n {
let j = (i + 1) % n;
let xi = polygon[[i, 0]];
let yi = polygon[[i, 1]];
let xj = polygon[[j, 0]];
let yj = polygon[[j, 1]];
let cross = xi * yj - xj * yi;
cx = cx + (xi + xj) * cross;
cy = cy + (yi + yj) * cross;
area = area + cross;
}
area = area / (T::one() + T::one());
if area.is_zero() {
let mut x_sum = T::zero();
let mut y_sum = T::zero();
for i in 0..n {
x_sum = x_sum + polygon[[i, 0]];
y_sum = y_sum + polygon[[i, 1]];
}
return vec![
x_sum / T::from(n).expect("Operation failed"),
y_sum / T::from(n).expect("Operation failed"),
];
}
let six = T::from(6).expect("Operation failed");
cx = cx / (six * area);
cy = cy / (six * area);
vec![cx.abs(), cy.abs()]
}
#[allow(dead_code)]
pub fn polygon_contains_polygon<T: Float>(
polygon_a: &ArrayView2<T>,
polygon_b: &ArrayView2<T>,
) -> bool {
if polygon_a.shape() == polygon_b.shape() {
let n = polygon_a.shape()[0];
let mut same = true;
for i in 0..n {
if polygon_a[[i, 0]] != polygon_b[[i, 0]] || polygon_a[[i, 1]] != polygon_b[[i, 1]] {
same = false;
break;
}
}
if same {
return true;
}
}
let n_b = polygon_b.shape()[0];
for i in 0..n_b {
let point = [polygon_b[[i, 0]], polygon_b[[i, 1]]];
if !point_in_polygon(&point, polygon_a) {
return false;
}
}
if polygon_a.shape() != polygon_b.shape() {
let n_a = polygon_a.shape()[0];
for i in 0..n_a {
let j = (i + 1) % n_a;
let a1 = [polygon_a[[i, 0]], polygon_a[[i, 1]]];
let a2 = [polygon_a[[j, 0]], polygon_a[[j, 1]]];
for k in 0..n_b {
let l = (k + 1) % n_b;
let b1 = [polygon_b[[k, 0]], polygon_b[[k, 1]]];
let b2 = [polygon_b[[l, 0]], polygon_b[[l, 1]]];
if segments_intersect(&a1, &a2, &b1, &b2)
&& !segments_overlap(&a1, &a2, &b1, &b2, T::epsilon())
&& !point_on_boundary(&a1, polygon_b, T::epsilon())
&& !point_on_boundary(&a2, polygon_b, T::epsilon())
&& !point_on_boundary(&b1, polygon_a, T::epsilon())
&& !point_on_boundary(&b2, polygon_a, T::epsilon())
{
return false;
}
}
}
}
true
}
#[allow(dead_code)]
fn segments_intersect<T: Float>(a1: &[T], a2: &[T], b1: &[T], b2: &[T]) -> bool {
let orientation = |p: &[T], q: &[T], r: &[T]| -> i32 {
let val = (q[1] - p[1]) * (r[0] - q[0]) - (q[0] - p[0]) * (r[1] - q[1]);
if val < T::zero() {
return 1; } else if val > T::zero() {
return 2; }
0 };
let on_segment = |p: &[T], q: &[T], r: &[T]| -> bool {
q[0] <= p[0].max(r[0])
&& q[0] >= p[0].min(r[0])
&& q[1] <= p[1].max(r[1])
&& q[1] >= p[1].min(r[1])
};
let o1 = orientation(a1, a2, b1);
let o2 = orientation(a1, a2, b2);
let o3 = orientation(b1, b2, a1);
let o4 = orientation(b1, b2, a2);
if o1 != o2 && o3 != o4 {
return true;
}
if o1 == 0 && on_segment(a1, b1, a2) {
return true;
}
if o2 == 0 && on_segment(a1, b2, a2) {
return true;
}
if o3 == 0 && on_segment(b1, a1, b2) {
return true;
}
if o4 == 0 && on_segment(b1, a2, b2) {
return true;
}
false
}
#[allow(dead_code)]
fn segments_overlap<T: Float>(a1: &[T], a2: &[T], b1: &[T], b2: &[T], epsilon: T) -> bool {
let cross = (a2[0] - a1[0]) * (b2[1] - b1[1]) - (a2[1] - a1[1]) * (b2[0] - b1[0]);
if cross.abs() > epsilon {
return false; }
let overlap_x = !(a2[0] < b1[0].min(b2[0]) - epsilon || a1[0] > b1[0].max(b2[0]) + epsilon);
let overlap_y = !(a2[1] < b1[1].min(b2[1]) - epsilon || a1[1] > b1[1].max(b2[1]) + epsilon);
overlap_x && overlap_y
}
#[allow(dead_code)]
pub fn is_simple_polygon<T: Float>(polygon: &ArrayView2<T>) -> bool {
let n = polygon.shape()[0];
if n < 3 {
return true; }
for i in 0..n {
let i1 = (i + 1) % n;
let a1 = [polygon[[i, 0]], polygon[[i, 1]]];
let a2 = [polygon[[i1, 0]], polygon[[i1, 1]]];
for j in i + 2..i + n - 1 {
let j_mod = j % n;
let j1 = (j_mod + 1) % n;
if j1 == i || j_mod == i1 {
continue;
}
let b1 = [polygon[[j_mod, 0]], polygon[[j_mod, 1]]];
let b2 = [polygon[[j1, 0]], polygon[[j1, 1]]];
if segments_intersect(&a1, &a2, &b1, &b2) {
return false; }
}
}
true
}
#[allow(dead_code)]
pub fn convex_hull_graham<T: Float + std::fmt::Debug>(points: &ArrayView2<T>) -> Array2<T> {
let n = points.shape()[0];
if n <= 3 {
return points.to_owned();
}
let mut lowest = 0;
for i in 1..n {
if points[[i, 1]] < points[[lowest, 1]]
|| (points[[i, 1]] == points[[lowest, 1]] && points[[i, 0]] < points[[lowest, 0]])
{
lowest = i;
}
}
let pivot_x = points[[lowest, 0]];
let pivot_y = points[[lowest, 1]];
let polar_angle = |x: T, y: T| -> T {
let dx = x - pivot_x;
let dy = y - pivot_y;
if dx.is_zero() && dy.is_zero() {
return T::neg_infinity();
}
dy.atan2(dx)
};
let mut indexedpoints: Vec<(usize, T)> = (0..n)
.map(|i| (i, polar_angle(points[[i, 0]], points[[i, 1]])))
.collect();
indexedpoints.sort_by(|a, b| {
let angle_cmp = a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal);
if angle_cmp == std::cmp::Ordering::Equal {
let dist_a =
(points[[a.0, 0]] - pivot_x).powi(2) + (points[[a.0, 1]] - pivot_y).powi(2);
let dist_b =
(points[[b.0, 0]] - pivot_x).powi(2) + (points[[b.0, 1]] - pivot_y).powi(2);
dist_a
.partial_cmp(&dist_b)
.unwrap_or(std::cmp::Ordering::Equal)
} else {
angle_cmp
}
});
let ccw = |i1: usize, i2: usize, i3: usize| -> bool {
let p1_x = points[[i1, 0]];
let p1_y = points[[i1, 1]];
let p2_x = points[[i2, 0]];
let p2_y = points[[i2, 1]];
let p3_x = points[[i3, 0]];
let p3_y = points[[i3, 1]];
let val = (p2_x - p1_x) * (p3_y - p1_y) - (p2_y - p1_y) * (p3_x - p1_x);
val > T::zero()
};
let mut hull_indices = Vec::new();
hull_indices.push(lowest);
for &(index_, _) in &indexedpoints {
if index_ == lowest {
continue;
}
while hull_indices.len() >= 2 {
let top = hull_indices.len() - 1;
if ccw(hull_indices[top - 1], hull_indices[top], index_) {
break;
}
hull_indices.pop();
}
hull_indices.push(index_);
}
let mut hull = Array2::zeros((hull_indices.len(), 2));
for (i, &idx) in hull_indices.iter().enumerate() {
hull[[i, 0]] = points[[idx, 0]];
hull[[i, 1]] = points[[idx, 1]];
}
hull
}
#[allow(dead_code)]
pub fn douglas_peucker_simplify<T: Float + std::fmt::Debug>(
polygon: &ArrayView2<T>,
tolerance: T,
) -> Array2<T> {
let n = polygon.shape()[0];
if n <= 2 {
return polygon.to_owned();
}
let mut keep = vec![false; n];
keep[0] = true;
keep[n - 1] = true;
douglas_peucker_recursive(polygon, 0, n - 1, tolerance, &mut keep);
let num_keep = keep.iter().filter(|&&x| x).count();
let mut simplified = Array2::zeros((num_keep, 2));
let mut simplified_idx = 0;
for i in 0..n {
if keep[i] {
simplified[[simplified_idx, 0]] = polygon[[i, 0]];
simplified[[simplified_idx, 1]] = polygon[[i, 1]];
simplified_idx += 1;
}
}
simplified
}
#[allow(dead_code)]
fn douglas_peucker_recursive<T: Float>(
polygon: &ArrayView2<T>,
start: usize,
end: usize,
tolerance: T,
keep: &mut [bool],
) {
if end <= start + 1 {
return;
}
let mut max_dist = T::zero();
let mut max_idx = start;
let startpoint = [polygon[[start, 0]], polygon[[start, 1]]];
let endpoint = [polygon[[end, 0]], polygon[[end, 1]]];
for i in start + 1..end {
let point = [polygon[[i, 0]], polygon[[i, 1]]];
let dist = perpendicular_distance(&point, &startpoint, &endpoint);
if dist > max_dist {
max_dist = dist;
max_idx = i;
}
}
if max_dist > tolerance {
keep[max_idx] = true;
douglas_peucker_recursive(polygon, start, max_idx, tolerance, keep);
douglas_peucker_recursive(polygon, max_idx, end, tolerance, keep);
}
}
#[allow(dead_code)]
fn perpendicular_distance<T: Float>(point: &[T; 2], line_start: &[T; 2], lineend: &[T; 2]) -> T {
let dx = lineend[0] - line_start[0];
let dy = lineend[1] - line_start[1];
if dx.is_zero() && dy.is_zero() {
let px = point[0] - line_start[0];
let py = point[1] - line_start[1];
return (px * px + py * py).sqrt();
}
let numerator = ((dy * (point[0] - line_start[0])) - (dx * (point[1] - line_start[1]))).abs();
let denominator = (dx * dx + dy * dy).sqrt();
numerator / denominator
}
#[allow(dead_code)]
pub fn visvalingam_whyatt_simplify<T: Float + std::fmt::Debug>(
polygon: &ArrayView2<T>,
min_area: T,
) -> Array2<T> {
let n = polygon.shape()[0];
if n <= 3 {
return polygon.to_owned();
}
let mut vertices: Vec<(usize, T)> = Vec::new();
let mut active = vec![true; n];
for i in 0..n {
let area = calculate_triangle_area(polygon, i, &active);
vertices.push((i, area));
}
vertices.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal));
let removal_candidates: Vec<usize> = vertices
.iter()
.filter(|(_, area)| *area < min_area)
.map(|(idx_, _)| *idx_)
.collect();
for vertex_idx in removal_candidates {
if count_active(&active) > 3 && active[vertex_idx] {
active[vertex_idx] = false;
let prev = find_previous_active(vertex_idx, &active, n);
let next = find_next_active(vertex_idx, &active, n);
if let (Some(prev_idx), Some(next_idx)) = (prev, next) {
update_vertex_area(polygon, prev_idx, &active, &mut vertices);
update_vertex_area(polygon, next_idx, &active, &mut vertices);
}
}
}
let num_active = count_active(&active);
let mut simplified = Array2::zeros((num_active, 2));
let mut simplified_idx = 0;
for i in 0..n {
if active[i] {
simplified[[simplified_idx, 0]] = polygon[[i, 0]];
simplified[[simplified_idx, 1]] = polygon[[i, 1]];
simplified_idx += 1;
}
}
simplified
}
#[allow(dead_code)]
fn calculate_triangle_area<T: Float>(
polygon: &ArrayView2<T>,
vertex_idx: usize,
active: &[bool],
) -> T {
let n = polygon.shape()[0];
let prev = find_previous_active(vertex_idx, active, n);
let next = find_next_active(vertex_idx, active, n);
match (prev, next) {
(Some(prev_idx), Some(next_idx)) => {
let p1 = [polygon[[prev_idx, 0]], polygon[[prev_idx, 1]]];
let p2 = [polygon[[vertex_idx, 0]], polygon[[vertex_idx, 1]]];
let p3 = [polygon[[next_idx, 0]], polygon[[next_idx, 1]]];
triangle_area(&p1, &p2, &p3)
}
_ => T::infinity(), }
}
#[allow(dead_code)]
fn triangle_area<T: Float>(p1: &[T; 2], p2: &[T; 2], p3: &[T; 2]) -> T {
((p1[0] * (p2[1] - p3[1]) + p2[0] * (p3[1] - p1[1]) + p3[0] * (p1[1] - p2[1]))
/ (T::one() + T::one()))
.abs()
}
#[allow(dead_code)]
fn find_previous_active(current: usize, active: &[bool], n: usize) -> Option<usize> {
for i in 1..n {
let idx = (current + n - i) % n;
if active[idx] && idx != current {
return Some(idx);
}
}
None
}
#[allow(dead_code)]
fn find_next_active(current: usize, active: &[bool], n: usize) -> Option<usize> {
for i in 1..n {
let idx = (current + i) % n;
if active[idx] && idx != current {
return Some(idx);
}
}
None
}
#[allow(dead_code)]
fn count_active(active: &[bool]) -> usize {
active.iter().filter(|&&x| x).count()
}
#[allow(dead_code)]
fn update_vertex_area<T: Float + std::fmt::Debug>(
polygon: &ArrayView2<T>,
vertex_idx: usize,
active: &[bool],
vertices: &mut [(usize, T)],
) {
let new_area = calculate_triangle_area(polygon, vertex_idx, active);
for (_idx, area) in vertices.iter_mut() {
if *_idx == vertex_idx {
*area = new_area;
break;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_relative_eq;
use scirs2_core::ndarray::array;
#[test]
fn testpoint_in_polygon() {
let square = array![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0],];
assert!(point_in_polygon(&[0.5, 0.5], &square.view()));
assert!(point_in_polygon(&[0.1, 0.1], &square.view()));
assert!(point_in_polygon(&[0.9, 0.9], &square.view()));
assert!(!point_in_polygon(&[1.5, 0.5], &square.view()));
assert!(!point_in_polygon(&[-0.5, 0.5], &square.view()));
assert!(!point_in_polygon(&[0.5, 1.5], &square.view()));
assert!(!point_in_polygon(&[0.5, -0.5], &square.view()));
assert!(point_in_polygon(&[0.0, 0.5], &square.view()));
assert!(point_in_polygon(&[1.0, 0.5], &square.view()));
assert!(point_in_polygon(&[0.5, 0.0], &square.view()));
assert!(point_in_polygon(&[0.5, 1.0], &square.view()));
let concave = array![[0.0, 0.0], [2.0, 0.0], [2.0, 2.0], [1.0, 1.0], [0.0, 2.0],];
assert!(!point_in_polygon(&[1.0, 1.5], &concave.view()));
assert!(point_in_polygon(&[0.5, 0.5], &concave.view()));
assert!(point_in_polygon(&[1.5, 0.5], &concave.view()));
}
#[test]
fn testpoint_on_boundary() {
let square = array![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0],];
let epsilon = 1e-10;
assert!(point_on_boundary(&[0.0, 0.5], &square.view(), epsilon));
assert!(point_on_boundary(&[1.0, 0.5], &square.view(), epsilon));
assert!(point_on_boundary(&[0.5, 0.0], &square.view(), epsilon));
assert!(point_on_boundary(&[0.5, 1.0], &square.view(), epsilon));
assert!(point_on_boundary(&[0.0, 0.0], &square.view(), epsilon));
assert!(point_on_boundary(&[1.0, 0.0], &square.view(), epsilon));
assert!(point_on_boundary(&[1.0, 1.0], &square.view(), epsilon));
assert!(point_on_boundary(&[0.0, 1.0], &square.view(), epsilon));
assert!(!point_on_boundary(&[0.5, 0.5], &square.view(), epsilon));
assert!(!point_on_boundary(&[1.5, 0.5], &square.view(), epsilon));
assert!(!point_on_boundary(&[0.0, 2.0], &square.view(), epsilon));
assert!(point_on_boundary(&[0.0, 0.5 + 1e-5], &square.view(), 1e-4));
}
#[test]
fn testpolygon_area() {
let square = array![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0],];
assert_relative_eq!(polygon_area(&square.view()), 1.0, epsilon = 1e-10);
let rectangle = array![[0.0, 0.0], [3.0, 0.0], [3.0, 2.0], [0.0, 2.0],];
assert_relative_eq!(polygon_area(&rectangle.view()), 6.0, epsilon = 1e-10);
let triangle = array![[0.0, 0.0], [1.0, 0.0], [0.0, 1.0],];
assert_relative_eq!(polygon_area(&triangle.view()), 0.5, epsilon = 1e-10);
let lshape = array![
[0.0, 0.0],
[2.0, 0.0],
[2.0, 1.0],
[1.0, 1.0],
[1.0, 2.0],
[0.0, 2.0],
];
assert_relative_eq!(polygon_area(&lshape.view()), 3.0, epsilon = 1e-10);
}
#[test]
fn testpolygon_centroid() {
let square = array![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0],];
let centroid = polygon_centroid(&square.view());
assert_relative_eq!(centroid[0], 0.5, epsilon = 1e-10);
assert_relative_eq!(centroid[1], 0.5, epsilon = 1e-10);
let rectangle = array![[0.0, 0.0], [3.0, 0.0], [3.0, 2.0], [0.0, 2.0],];
let centroid = polygon_centroid(&rectangle.view());
assert_relative_eq!(centroid[0], 1.5, epsilon = 1e-10);
assert_relative_eq!(centroid[1], 1.0, epsilon = 1e-10);
let triangle = array![[0.0, 0.0], [1.0, 0.0], [0.0, 1.0],];
let centroid = polygon_centroid(&triangle.view());
assert_relative_eq!(centroid[0], 1.0 / 3.0, epsilon = 1e-10);
assert_relative_eq!(centroid[1], 1.0 / 3.0, epsilon = 1e-10);
}
#[test]
fn testpolygon_contains_polygon() {
let outer = array![[0.0, 0.0], [3.0, 0.0], [3.0, 3.0], [0.0, 3.0],];
let inner = array![[1.0, 1.0], [2.0, 1.0], [2.0, 2.0], [1.0, 2.0],];
assert!(polygon_contains_polygon(&outer.view(), &inner.view()));
assert!(!polygon_contains_polygon(&inner.view(), &outer.view()));
let overlap = array![[2.0, 2.0], [4.0, 2.0], [4.0, 4.0], [2.0, 4.0],];
assert!(!polygon_contains_polygon(&outer.view(), &overlap.view()));
assert!(!polygon_contains_polygon(&overlap.view(), &outer.view()));
assert!(polygon_contains_polygon(&outer.view(), &outer.view()));
}
#[test]
fn test_is_simple_polygon() {
let square = array![[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0],];
assert!(is_simple_polygon(&square.view()));
let bowtie = array![[0.0, 0.0], [1.0, 1.0], [0.0, 1.0], [1.0, 0.0],];
assert!(!is_simple_polygon(&bowtie.view()));
let complex = array![[0.0, 0.0], [2.0, 0.0], [2.0, 2.0], [1.0, 1.0], [0.0, 2.0],];
assert!(is_simple_polygon(&complex.view()));
}
#[test]
fn test_convex_hull_graham() {
let points = array![
[0.0, 0.0],
[1.0, 0.0],
[0.5, 0.5], [1.0, 1.0],
[0.0, 1.0],
];
let hull = convex_hull_graham(&points.view());
assert_eq!(hull.shape()[0], 4);
let mut found_inside = false;
for i in 0..hull.shape()[0] {
if (hull[[i, 0]] - 0.5).abs() < 1e-10 && (hull[[i, 1]] - 0.5).abs() < 1e-10 {
found_inside = true;
break;
}
}
assert!(!found_inside);
let mut circlepoints = Array2::zeros((8, 2));
for i in 0..8 {
let angle = 2.0 * std::f64::consts::PI * (i as f64) / 8.0;
circlepoints[[i, 0]] = angle.cos();
circlepoints[[i, 1]] = angle.sin();
}
let allpoints = array![
[0.0, 0.0], [0.5, 0.0], [0.0, 0.5], [-0.5, 0.0], [0.0, -0.5], [1.0, 0.0],
[
std::f64::consts::FRAC_1_SQRT_2,
std::f64::consts::FRAC_1_SQRT_2
],
[0.0, 1.0],
[
-std::f64::consts::FRAC_1_SQRT_2,
std::f64::consts::FRAC_1_SQRT_2
],
[-1.0, 0.0],
[
-std::f64::consts::FRAC_1_SQRT_2,
-std::f64::consts::FRAC_1_SQRT_2
],
[0.0, -1.0],
[
std::f64::consts::FRAC_1_SQRT_2,
-std::f64::consts::FRAC_1_SQRT_2
],
];
let hull = convex_hull_graham(&allpoints.view());
assert_eq!(hull.shape()[0], 8);
}
}