mod bowyer_watson_2d;
mod bowyer_watson_3d;
mod bowyer_watson_nd;
mod constrained;
mod queries;
#[cfg(test)]
mod tests;
use crate::error::{SpatialError, SpatialResult};
use scirs2_core::ndarray::Array2;
use std::collections::HashMap;
use std::fmt::Debug;
pub use constrained::*;
pub use queries::*;
pub struct Delaunay {
pub(crate) points: Array2<f64>,
pub(crate) ndim: usize,
pub(crate) npoints: usize,
pub(crate) simplices: Vec<Vec<usize>>,
pub(crate) neighbors: Vec<Vec<i64>>,
pub(crate) constraints: Vec<(usize, usize)>,
}
impl Debug for Delaunay {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Delaunay")
.field("points", &self.points.shape())
.field("ndim", &self.ndim)
.field("npoints", &self.npoints)
.field("simplices", &self.simplices.len())
.field("neighbors", &self.neighbors.len())
.field("constraints", &self.constraints.len())
.finish()
}
}
impl Clone for Delaunay {
fn clone(&self) -> Self {
Self {
points: self.points.clone(),
ndim: self.ndim,
npoints: self.npoints,
simplices: self.simplices.clone(),
neighbors: self.neighbors.clone(),
constraints: self.constraints.clone(),
}
}
}
impl Delaunay {
pub fn new(points: &Array2<f64>) -> SpatialResult<Self> {
let npoints = points.nrows();
let ndim = points.ncols();
if npoints <= ndim {
return Err(SpatialError::ValueError(format!(
"Need at least {} points in {} dimensions for triangulation",
ndim + 1,
ndim
)));
}
if ndim == 2 && npoints == 3 {
let simplex = vec![0, 1, 2];
let simplices = vec![simplex];
let neighbors = vec![vec![-1, -1, -1]];
return Ok(Delaunay {
points: points.clone(),
ndim,
npoints,
simplices,
neighbors,
constraints: Vec::new(),
});
}
let simplices = match ndim {
2 => bowyer_watson_2d::bowyer_watson_2d(points)?,
3 => bowyer_watson_3d::bowyer_watson_3d(points)?,
_ => bowyer_watson_nd::bowyer_watson_nd(points, ndim)?,
};
let neighbors = Self::calculate_neighbors(&simplices, ndim + 1);
Ok(Delaunay {
points: points.clone(),
ndim,
npoints,
simplices,
neighbors,
constraints: Vec::new(),
})
}
pub(crate) fn calculate_neighbors(simplices: &[Vec<usize>], n: usize) -> Vec<Vec<i64>> {
let nsimplex = simplices.len();
let mut neighbors = vec![vec![-1; n]; nsimplex];
let mut face_to_simplex: HashMap<Vec<usize>, Vec<(usize, usize)>> = HashMap::new();
for (i, simplex) in simplices.iter().enumerate() {
for j in 0..n {
let mut face: Vec<usize> = simplex
.iter()
.enumerate()
.filter(|&(k_, _)| k_ != j)
.map(|(_, &v)| v)
.collect();
face.sort();
face_to_simplex.entry(face).or_default().push((i, j));
}
}
for (_, simplex_info) in face_to_simplex.iter() {
if simplex_info.len() == 2 {
let (i1, j1) = simplex_info[0];
let (i2, j2) = simplex_info[1];
neighbors[i1][j1] = i2 as i64;
neighbors[i2][j2] = i1 as i64;
}
}
neighbors
}
pub fn npoints(&self) -> usize {
self.npoints
}
pub fn ndim(&self) -> usize {
self.ndim
}
pub fn points(&self) -> &Array2<f64> {
&self.points
}
pub fn simplices(&self) -> &[Vec<usize>] {
&self.simplices
}
pub fn neighbors(&self) -> &[Vec<i64>] {
&self.neighbors
}
pub(crate) fn compute_neighbors(&mut self) {
self.neighbors = Self::calculate_neighbors(&self.simplices, self.ndim + 1);
}
}