#![warn(missing_docs)]
#![warn(rustdoc::missing_doc_code_examples)]
pub mod polygon;
pub mod delaunator;
use maybe_parallel_iterator::{IntoMaybeParallelIterator, IntoMaybeParallelRefIterator};
use std::{f64, usize};
use crate::delaunator::*;
use crate::polygon::*;
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct CentroidDiagram<C: Coord + Vector<C>> {
pub sites: Vec<C>,
pub delaunay: Triangulation,
pub centers: Vec<C>,
pub cells: Vec<Polygon<C>>,
pub neighbors: Vec<Vec<usize>>,
}
impl<C: Coord + Vector<C>> CentroidDiagram<C> {
pub fn new(points: &[C]) -> Option<Self> {
let delaunay = triangulate(points)?;
let centers = calculate_centroids(points, &delaunay);
let cells = CentroidDiagram::calculate_polygons(points, ¢ers, &delaunay);
let neighbors = calculate_neighbors(points, &delaunay);
Some(CentroidDiagram {
sites: points.to_vec(),
delaunay,
centers,
cells,
neighbors,
})
}
pub fn from_tuple(coords: &[(f64, f64)]) -> Option<Self> {
let points: Vec<C> = coords.iter().map(|p| C::from_xy(p.0, p.1)).collect();
CentroidDiagram::new(&points)
}
fn calculate_polygons(
points: &[C],
centers: &[C],
delaunay: &Triangulation,
) -> Vec<Polygon<C>> {
let mut polygons: Vec<Polygon<C>> = vec![];
for t in 0..points.len() {
let incoming = delaunay.inedges[t];
let edges = edges_around_point(incoming, delaunay);
let triangles: Vec<usize> = edges.into_iter().map(triangle_of_edge).collect();
let polygon: Vec<C> = triangles.into_iter().map(|t| centers[t].clone()).collect();
polygons.push(Polygon::from_points(polygon));
}
polygons
}
}
fn helper_points<C: Coord>(polygon: &Polygon<C>) -> Vec<C> {
let mut points = vec![];
let mut min = Point{x: f64::MAX, y: f64::MAX};
let mut max = Point{x: f64::MIN, y: f64::MIN};
for point in polygon.points() {
if point.x() < min.x() {
min.x = point.x();
}
if point.x() > max.x() {
max.x = point.x();
}
if point.y() < min.y() {
min.y = point.y();
}
if point.y() > max.y() {
max.y = point.y();
}
}
let width = max.x() - min.x();
let height = max.y() - min.y();
points.push(C::from_xy(min.x() - width, min.y() + height / 2.0));
points.push(C::from_xy(max.x() + width, min.y() + height / 2.0));
points.push(C::from_xy(min.x() + width / 2.0, min.y() - height));
points.push(C::from_xy(min.x() + width / 2.0, max.y() + height));
points
}
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub struct VoronoiDiagram<C: Coord + Vector<C>> {
pub sites: Vec<C>,
pub delaunay: Triangulation,
pub centers: Vec<C>,
cells: Vec<Polygon<C>>,
pub neighbors: Vec<Vec<usize>>,
num_helper_points: usize,
}
impl<C: Coord + Vector<C>> VoronoiDiagram<C> {
pub fn new(min: &C, max: &C, points: &[C]) -> Option<Self> {
let clip_points = vec![C::from_xy(min.x(), min.y()), C::from_xy(max.x(), min.y()), C::from_xy(max.x(), max.y()), C::from_xy(min.x(), max.y())];
let clip_polygon = polygon::Polygon::from_points(clip_points);
VoronoiDiagram::with_bounding_polygon(points.to_vec(), &clip_polygon)
}
pub fn with_bounding_polygon(mut points: Vec<C>, clip_polygon: &Polygon<C>) -> Option<Self> {
let mut helper_points = helper_points(&clip_polygon);
let num_helper_points = helper_points.len();
points.append(&mut helper_points);
VoronoiDiagram::with_helper_points(points, clip_polygon, num_helper_points)
}
fn with_helper_points(points: Vec<C>, clip_polygon: &Polygon<C>, num_helper_points: usize) -> Option<Self> {
let delaunay = triangulate(&points)?;
let centers = calculate_circumcenters(&points, &delaunay);
let cells =
VoronoiDiagram::calculate_polygons(&points, ¢ers, &delaunay, &clip_polygon);
let neighbors = calculate_neighbors(&points, &delaunay);
Some(VoronoiDiagram {
sites: points,
delaunay,
centers,
cells,
neighbors,
num_helper_points,
})
}
pub fn from_tuple(min: &(f64, f64), max: &(f64, f64), coords: &[(f64, f64)]) -> Option<Self> {
let points: Vec<C> = coords.iter().map(|p| C::from_xy(p.0, p.1)).collect();
let clip_points = vec![C::from_xy(min.0, min.1), C::from_xy(max.0, min.1),
C::from_xy(max.0, max.1), C::from_xy(min.0, max.1)];
let clip_polygon = polygon::Polygon::from_points(clip_points);
VoronoiDiagram::with_bounding_polygon(points, &clip_polygon)
}
pub fn cells(&self) -> &[Polygon<C>] {
&self.cells[..self.cells.len()-self.num_helper_points]
}
fn calculate_polygons(
points: &[C],
centers: &[C],
delaunay: &Triangulation,
clip_polygon: &Polygon<C>,
) -> Vec<Polygon<C>> {
points.maybe_par_iter().enumerate().map(|(t, _point)| {
let incoming = delaunay.inedges[t];
let edges = edges_around_point(incoming, delaunay);
let triangles: Vec<usize> = edges.into_iter().map(triangle_of_edge).collect();
let polygon: Vec<C> = triangles.into_iter().map(|t| centers[t].clone()).collect();
let polygon = polygon::Polygon::from_points(polygon);
let polygon = polygon::sutherland_hodgman(&polygon, &clip_polygon);
polygon
}).collect()
}
}
fn calculate_centroids<C: Coord + Vector<C>>(points: &[C], delaunay: &Triangulation) -> Vec<C> {
let num_triangles = delaunay.len();
let mut centroids = Vec::with_capacity(num_triangles);
for t in 0..num_triangles {
let mut sum = Point { x: 0., y: 0. };
for i in 0..3 {
let s = 3 * t + i; let p = &points[delaunay.triangles[s]];
sum.x += p.x();
sum.y += p.y();
}
centroids.push(C::from_xy(
sum.x / 3.,
sum.y / 3.,
));
}
centroids
}
fn calculate_circumcenters<C: Coord + Vector<C>>(points: &[C], delaunay: &Triangulation) -> Vec<C> {
(0..delaunay.len()).into_maybe_par_iter().map(|t| {
let v: Vec<C> = points_of_triangle(t, delaunay)
.into_iter()
.map(|p| points[p].clone())
.collect();
match circumcenter(&v[0], &v[1], &v[2]) {
Some(c) => c,
None => C::from_xy(0., 0.)
}
}).collect()
}
fn calculate_neighbors<C: Coord + Vector<C>>(points: &[C], delaunay: &Triangulation) -> Vec<Vec<usize>> {
points.maybe_par_iter().enumerate().map(|(t, _point)| {
let mut neighbours: Vec<usize> = vec![];
let e0 = delaunay.inedges[t];
if e0 != INVALID_INDEX {
let mut e = e0;
loop {
neighbours.push(delaunay.triangles[e]);
e = next_halfedge(e);
if delaunay.triangles[e] != t {
break;
}
e = delaunay.halfedges[e];
if e == INVALID_INDEX {
neighbours.push(delaunay.triangles[delaunay.outedges[t]]);
break;
}
if e == e0 {
break;
}
}
}
neighbours
}).collect()
}