use crate::error::SpatialResult;
use scirs2_core::ndarray::{Array2, ArrayView2};
pub fn cross_product_2d(p1: [f64; 2], p2: [f64; 2], p3: [f64; 2]) -> f64 {
(p2[0] - p1[0]) * (p3[1] - p1[1]) - (p2[1] - p1[1]) * (p3[0] - p1[0])
}
pub fn distance_squared_2d(p1: [f64; 2], p2: [f64; 2]) -> f64 {
(p2[0] - p1[0]).powi(2) + (p2[1] - p1[1]).powi(2)
}
pub fn compute_2d_hull_equations(
points: &ArrayView2<'_, f64>,
vertex_indices: &[usize],
) -> Array2<f64> {
let n = vertex_indices.len();
let mut equations = Array2::zeros((n, 3));
let mut centroid = [0.0, 0.0];
for &idx in vertex_indices {
centroid[0] += points[[idx, 0]];
centroid[1] += points[[idx, 1]];
}
centroid[0] /= n as f64;
centroid[1] /= n as f64;
for i in 0..n {
let j = (i + 1) % n;
let p1 = [
points[[vertex_indices[i], 0]],
points[[vertex_indices[i], 1]],
];
let p2 = [
points[[vertex_indices[j], 0]],
points[[vertex_indices[j], 1]],
];
let dx = p2[0] - p1[0];
let dy = p2[1] - p1[1];
let len = (dx * dx + dy * dy).sqrt();
if len < 1e-10 {
continue;
}
let mut nx = -dy / len;
let mut ny = dx / len;
let mut offset = -(nx * p1[0] + ny * p1[1]);
let centroid_val = nx * centroid[0] + ny * centroid[1] + offset;
if centroid_val > 0.0 {
nx = -nx;
ny = -ny;
offset = -offset;
}
equations[[i, 0]] = nx;
equations[[i, 1]] = ny;
equations[[i, 2]] = offset;
}
equations
}
pub fn compute_polygon_area(
points: &ArrayView2<'_, f64>,
vertex_indices: &[usize],
) -> SpatialResult<f64> {
if vertex_indices.len() < 3 {
return Ok(0.0);
}
let mut area = 0.0;
let n = vertex_indices.len();
for i in 0..n {
let j = (i + 1) % n;
let xi = points[[vertex_indices[i], 0]];
let yi = points[[vertex_indices[i], 1]];
let xj = points[[vertex_indices[j], 0]];
let yj = points[[vertex_indices[j], 1]];
area += xi * yj - xj * yi;
}
Ok(area.abs() / 2.0)
}
pub fn compute_polygon_perimeter(
points: &ArrayView2<'_, f64>,
vertex_indices: &[usize],
) -> SpatialResult<f64> {
if vertex_indices.len() < 2 {
return Ok(0.0);
}
let mut perimeter = 0.0;
let n = vertex_indices.len();
for i in 0..n {
let j = (i + 1) % n;
let xi = points[[vertex_indices[i], 0]];
let yi = points[[vertex_indices[i], 1]];
let xj = points[[vertex_indices[j], 0]];
let yj = points[[vertex_indices[j], 1]];
let dx = xj - xi;
let dy = yj - yi;
perimeter += (dx * dx + dy * dy).sqrt();
}
Ok(perimeter)
}
pub fn is_counterclockwise(p1: [f64; 2], p2: [f64; 2], p3: [f64; 2]) -> bool {
cross_product_2d(p1, p2, p3) > 0.0
}
pub fn polar_angle(reference: [f64; 2], point: [f64; 2]) -> f64 {
(point[1] - reference[1]).atan2(point[0] - reference[0])
}
#[cfg(test)]
mod tests {
use super::*;
use scirs2_core::ndarray::arr2;
#[test]
fn test_cross_product_2d() {
let p1 = [0.0, 0.0];
let p2 = [1.0, 0.0];
let p3 = [0.0, 1.0];
let cross = cross_product_2d(p1, p2, p3);
assert!(cross > 0.0);
let cross_cw = cross_product_2d(p1, p3, p2);
assert!(cross_cw < 0.0); }
#[test]
fn test_distance_squared_2d() {
let p1 = [0.0, 0.0];
let p2 = [3.0, 4.0];
let dist_sq = distance_squared_2d(p1, p2);
assert_eq!(dist_sq, 25.0);
}
#[test]
fn test_polygon_area() {
let points = arr2(&[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]);
let vertices = vec![0, 1, 2, 3];
let area = compute_polygon_area(&points.view(), &vertices).expect("Operation failed");
assert!((area - 1.0).abs() < 1e-10);
}
#[test]
fn test_polygon_perimeter() {
let points = arr2(&[[0.0, 0.0], [1.0, 0.0], [1.0, 1.0], [0.0, 1.0]]);
let vertices = vec![0, 1, 2, 3];
let perimeter =
compute_polygon_perimeter(&points.view(), &vertices).expect("Operation failed");
assert!((perimeter - 4.0).abs() < 1e-10);
}
#[test]
fn test_counterclockwise() {
let p1 = [0.0, 0.0];
let p2 = [1.0, 0.0];
let p3 = [0.0, 1.0];
assert!(is_counterclockwise(p1, p2, p3));
assert!(!is_counterclockwise(p1, p3, p2));
}
#[test]
fn test_polar_angle() {
let origin = [0.0, 0.0];
let point = [1.0, 1.0];
let angle = polar_angle(origin, point);
assert!((angle - std::f64::consts::PI / 4.0).abs() < 1e-10);
}
}