use crate::error::SpatialResult;
use scirs2_core::ndarray::Array2;
pub(crate) fn bowyer_watson_2d(points: &Array2<f64>) -> SpatialResult<Vec<Vec<usize>>> {
let npoints = points.nrows();
let mut min_x = f64::INFINITY;
let mut max_x = f64::NEG_INFINITY;
let mut min_y = f64::INFINITY;
let mut max_y = f64::NEG_INFINITY;
for i in 0..npoints {
let x = points[[i, 0]];
let y = points[[i, 1]];
min_x = min_x.min(x);
max_x = max_x.max(x);
min_y = min_y.min(y);
max_y = max_y.max(y);
}
let dx = max_x - min_x;
let dy = max_y - min_y;
let delta_max = dx.max(dy);
let mid_x = (min_x + max_x) / 2.0;
let mid_y = (min_y + max_y) / 2.0;
let margin = 20.0;
let super_p0 = [mid_x - margin * delta_max, mid_y - delta_max];
let super_p1 = [mid_x, mid_y + margin * delta_max];
let super_p2 = [mid_x + margin * delta_max, mid_y - delta_max];
let mut all_points: Vec<[f64; 2]> = Vec::with_capacity(npoints + 3);
for i in 0..npoints {
all_points.push([points[[i, 0]], points[[i, 1]]]);
}
all_points.push(super_p0);
all_points.push(super_p1);
all_points.push(super_p2);
let mut triangles: Vec<[usize; 3]> = vec![[npoints, npoints + 1, npoints + 2]];
for point_idx in 0..npoints {
let point = all_points[point_idx];
let mut bad_triangles: Vec<usize> = Vec::new();
for (tri_idx, tri) in triangles.iter().enumerate() {
let p0 = all_points[tri[0]];
let p1 = all_points[tri[1]];
let p2 = all_points[tri[2]];
if point_in_circumcircle_2d(point, p0, p1, p2) {
bad_triangles.push(tri_idx);
}
}
let mut polygon_edges: Vec<[usize; 2]> = Vec::new();
for &tri_idx in &bad_triangles {
let tri = triangles[tri_idx];
let edges = [[tri[0], tri[1]], [tri[1], tri[2]], [tri[2], tri[0]]];
for edge in &edges {
let mut is_shared = false;
for &other_idx in &bad_triangles {
if other_idx == tri_idx {
continue;
}
let other_tri = triangles[other_idx];
if triangle_has_edge_2d(&other_tri, edge[0], edge[1]) {
is_shared = true;
break;
}
}
if !is_shared {
polygon_edges.push(*edge);
}
}
}
bad_triangles.sort_by(|a, b| b.cmp(a));
for tri_idx in bad_triangles {
triangles.remove(tri_idx);
}
for edge in polygon_edges {
triangles.push([point_idx, edge[0], edge[1]]);
}
}
let super_vertices = [npoints, npoints + 1, npoints + 2];
triangles.retain(|tri| !tri.iter().any(|&v| super_vertices.contains(&v)));
let simplices: Vec<Vec<usize>> = triangles.iter().map(|t| t.to_vec()).collect();
Ok(simplices)
}
fn triangle_has_edge_2d(tri: &[usize; 3], v1: usize, v2: usize) -> bool {
let edges = [[tri[0], tri[1]], [tri[1], tri[2]], [tri[2], tri[0]]];
for edge in &edges {
if (edge[0] == v1 && edge[1] == v2) || (edge[0] == v2 && edge[1] == v1) {
return true;
}
}
false
}
fn point_in_circumcircle_2d(p: [f64; 2], a: [f64; 2], b: [f64; 2], c: [f64; 2]) -> bool {
let ax = a[0] - p[0];
let ay = a[1] - p[1];
let bx = b[0] - p[0];
let by = b[1] - p[1];
let cx = c[0] - p[0];
let cy = c[1] - p[1];
let det = (ax * ax + ay * ay) * (bx * cy - cx * by) - (bx * bx + by * by) * (ax * cy - cx * ay)
+ (cx * cx + cy * cy) * (ax * by - bx * ay);
let orientation = (b[0] - a[0]) * (c[1] - a[1]) - (b[1] - a[1]) * (c[0] - a[0]);
if orientation > 0.0 {
det > 0.0
} else {
det < 0.0
}
}