use delaunator::{EMPTY, next_halfedge, Triangulation};
use crate::{utils::{triangle_of_edge}, BoundingBox, bounding_box};
use super::{ClipBehavior, Point, iterator::EdgesAroundSiteIterator, utils::{self, site_of_incoming}};
const VORONOI_INFINITY: f64 = 1e+10_f64;
#[derive(Debug)]
pub struct CellBuilder<'t> {
triangulation: &'t Triangulation,
sites: &'t Vec<Point>,
vertices: Vec<Point>,
site_to_incoming_leftmost_halfedge: Vec<usize>,
corner_ownership: Vec<usize>,
is_vertex_inside_bounding_box: Vec<bool>,
bounding_box: BoundingBox,
clip_behavior: ClipBehavior,
first_corner_index: usize,
number_of_circumcenters: usize,
}
pub struct CellBuilderResult {
pub cells: Vec<Vec<usize>>,
pub vertices: Vec<Point>,
pub site_to_incoming_leftmost_halfedge: Vec<usize>,
}
impl<'t> CellBuilder<'t> {
pub fn new(triangulation: &'t Triangulation, sites: &'t Vec<Point>, vertices: Vec<Point>, bounding_box: BoundingBox, clip_behavior: ClipBehavior) -> Self {
let site_to_incoming_leftmost_halfedge = calculate_incoming_edges(triangulation, sites.len());
let is_vertex_inside_bounding_box: Vec<bool> = vertices.iter().map(|c| bounding_box.is_inside(c)).collect();
let corner_ownership = if clip_behavior == ClipBehavior::Clip {
calculate_corner_ownership(&bounding_box.corners(), &triangulation, sites, &site_to_incoming_leftmost_halfedge)
} else {
Vec::with_capacity(0)
};
Self {
triangulation,
sites,
site_to_incoming_leftmost_halfedge,
is_vertex_inside_bounding_box,
corner_ownership,
first_corner_index: 0,
number_of_circumcenters: vertices.len(),
vertices,
bounding_box,
clip_behavior
}
}
pub fn build(mut self) -> CellBuilderResult {
if self.clip_behavior == ClipBehavior::Clip {
self.calculate_corners();
}
let cells = self.build_cells();
CellBuilderResult {
vertices: self.vertices,
site_to_incoming_leftmost_halfedge: self.site_to_incoming_leftmost_halfedge,
cells
}
}
pub fn calculate_corners(&mut self) {
self.first_corner_index = self.vertices.len();
for corner in self.bounding_box.corners() {
self.vertices.push(corner);
}
}
fn build_cells(&mut self) -> Vec<Vec<usize>> {
let triangulation = self.triangulation;
let sites: &Vec<Point> = self.sites;
let num_of_sites = sites.len();
let mut cells: Vec<Vec<usize>> = vec![Vec::new(); num_of_sites];
let mut tmp_cell = Vec::new();
if self.clip_behavior == ClipBehavior::Clip {
for (&a, &b) in triangulation.hull.iter().zip(triangulation.hull.iter().cycle().skip(1)) {
let hull_edge = self.site_to_incoming_leftmost_halfedge[b];
let extension = self.extend_voronoi_vertex(hull_edge);
cells[b].push(extension);
cells[b].extend(EdgesAroundSiteIterator::new(triangulation, hull_edge).map(utils::triangle_of_edge));
cells[a].push(extension);
}
for &hull_site in &triangulation.hull {
let hull_cell = &mut cells[hull_site];
tmp_cell.clear();
std::mem::swap(hull_cell, &mut tmp_cell);
self.clip_cell(&tmp_cell, hull_cell, hull_site);
}
}
for edge in 0..triangulation.triangles.len() {
let site = site_of_incoming(triangulation, edge);
let cell = &mut cells[site];
if cell.len() == 0 {
#[cfg(debug_logs)] println!();
#[cfg(debug_logs)] println!("Site: {site}.");
let circumcenter_iter = EdgesAroundSiteIterator::new(triangulation, edge).map(utils::triangle_of_edge);
if self.clip_behavior == ClipBehavior::Clip {
tmp_cell.clear();
tmp_cell.extend(circumcenter_iter);
self.clip_cell(&tmp_cell, cell, site);
} else {
cell.extend(circumcenter_iter);
cell.reverse();
}
#[cfg(debug_logs)] println!(" [{site}/{edge}] Cell: {:?}", cell);
}
}
cells
}
fn clip_cell(&mut self, tmp_cell: &Vec<usize>, cell: &mut Vec<usize>, site: usize) {
#[cfg(debug_logs)] println!(" Temp: {:?}", tmp_cell);
let (first_index, first, first_inside) = if let Some(inside) = tmp_cell.iter().enumerate().filter(|(_, &c)| self.is_vertex_inside_bounding_box(c)).next() {
(inside.0, *inside.1, true)
} else {
(0, tmp_cell[0], false)
};
let mut prev = first;
let mut prev_inside = first_inside;
let mut cell_open = false; #[cfg(debug_logs)] println!(" Start: Temp[{first_index}] = {first}.");
for &c in tmp_cell.iter().rev().cycle().skip(tmp_cell.len() - first_index).take(tmp_cell.len()) {
let inside = self.is_vertex_inside_bounding_box(c);
match (prev_inside, inside) {
(false, false) => {
let (first_clip, second_clip) = self.clip_voronoi_edge(prev, c);
if let Some(first_clip) = first_clip {
if cell_open {
self.insert_edge_and_wrap_around_corners(site, cell,
*cell.last().expect("Cell must not be empty because we started from a vertex inside the bounding box."),
first_clip);
#[cfg(debug_logs)] println!(" [{site}] Edge {prev} -> {c}. Edge outside box. The box was open.");
}
#[cfg(debug_logs)] println!(" [{site}] Edge {prev} -> {c}. First clip: {first_clip}. Second clip: {}", second_clip.unwrap_or_default());
self.insert_edge_and_wrap_around_corners(site, cell,
first_clip,
second_clip.expect("Two intersection points need to occur when a line crosses the bounding box"));
#[cfg(debug_logs)] println!(" [{site}] Edge {prev} -> {c}. Edge outside box. Entered at {} and left at {}", first_clip, second_clip.unwrap());
} else {
#[cfg(debug_logs)] println!(" [{site}] Edge {prev} -> {c}. Edge outside box, no intersection.");
}
},
(false, true) => {
let (first_clip, second_clip) = self.clip_voronoi_edge(c, prev);
let first_clip = first_clip.expect("Edge crosses box, intersection must exist.");
debug_assert!(second_clip.is_none(), "Cannot have two intersections with the bounding box when one of the edge's vertex is inside the bounding box");
self.insert_edge_and_wrap_around_corners(site, cell,
*cell.last().expect("Cell must not be empty because we started from a vertex inside the bounding box."),
first_clip);
cell_open = false;
#[cfg(debug_logs)] println!(" [{site}] Edge {prev} -> {c}: Entering box at {first_clip} (previously left from {})");
},
(true, false) => {
let (first_clip, second_clip) = self.clip_voronoi_edge(prev, c);
let first_clip = first_clip.expect("Edge crosses box, intersection must exist.");
debug_assert!(second_clip.is_none(), "Cannot have two intersections with the bounding box when one of the edge's vertex is inside the bounding box");
cell.push(prev);
cell.push(first_clip);
cell_open = true;
#[cfg(debug_logs)] println!(" [{site}] Edge {prev} -> {c}: Leaving box. Added {prev} and clipped at {}", cell.last().unwrap());
},
(true, true) => {
#[cfg(debug_logs)] println!(" [{site}] Edge {prev} -> {c}: Inside box. Added {prev}.");
cell.push(prev);
},
}
prev = c;
prev_inside = inside;
}
}
fn clip_voronoi_edge(&mut self, a: usize, b: usize) -> (Option<usize>, Option<usize>) {
let a_pos = &self.vertices[a];
let b_pos = &self.vertices[b];
let a_to_b = &Point { x: b_pos.x - a_pos.x, y: b_pos.y - a_pos.y };
let result = match self.bounding_box.project_ray(a_pos, a_to_b) {
(Some(first_clip), None) => {
(Some(self.add_new_vertex(first_clip)), None)
},
(Some(first_clip), Some(second_clip)) => {
if bounding_box::order_points_on_ray(a_pos, a_to_b, Some(b_pos.clone()), Some(first_clip.clone())).0 == Some(first_clip.clone()) {
(Some(self.add_new_vertex(first_clip)), Some(self.add_new_vertex(second_clip)))
} else {
(None, None)
}
},
(None, Some(_)) => panic!("project_ray must not return second intersection without returning the first intersection"),
(None, None) => (None, None),
};
result
}
fn insert_edge_and_wrap_around_corners(&mut self, site: usize, cell: &mut Vec<usize>, first_clip: usize, second_clip: usize) {
if cell.last() != Some(&first_clip) {
cell.push(first_clip);
}
let first_edge = self.bounding_box.which_edge(&self.vertices[first_clip]).expect("First clipped value is expected to be on the edge of the bounding box.");
let second_edge = self.bounding_box.which_edge(&self.vertices[second_clip]).expect("Second clipped value is expected to be on the edge of the bounding box.");
#[cfg(debug_logs)] let len = cell.len();
if self.corner_ownership[first_edge] == site {
let mut edge = first_edge;
while edge != second_edge && self.corner_ownership[edge] == site {
cell.push(self.first_corner_index + edge);
self.corner_ownership[edge] = EMPTY; edge = bounding_box::next_edge(edge);
}
cell.push(second_clip);
} else if self.corner_ownership[second_edge] == site {
cell.push(second_clip);
let mut edge = second_edge;
while edge != first_edge && self.corner_ownership[edge] == site {
cell.push(self.first_corner_index + edge);
self.corner_ownership[edge] = EMPTY;
edge = bounding_box::next_edge(edge);
}
} else {
cell.push(second_clip);
}
#[cfg(debug_logs)] println!(" [{site}] Edge {first_clip} ({first_edge}) -> {second_clip} ({second_edge}). Wrapping around {:?}", &cell[len-1..]);
}
fn extend_voronoi_vertex(&mut self, hull_edge: usize) -> usize {
let circumcenter = triangle_of_edge(hull_edge);
let circumcenter_pos = &self.vertices[circumcenter];
let (a, b) = (self.triangulation.triangles[hull_edge], self.triangulation.triangles[next_halfedge(hull_edge)]);
let a_pos = &self.sites[a];
let b_pos = &self.sites[b];
let mut orthogonal = Point { x: a_pos.y - b_pos.y , y: b_pos.x - a_pos.x };
let ortho_length = (orthogonal.x * orthogonal.x + orthogonal.y * orthogonal.y).sqrt();
orthogonal.x *= 1.0 / ortho_length;
orthogonal.y *= 1.0 / ortho_length;
let projected = Point { x: circumcenter_pos.x + orthogonal.x * VORONOI_INFINITY, y: circumcenter_pos.y + orthogonal.y * VORONOI_INFINITY };
let v = self.add_new_vertex(projected);
#[cfg(debug_logs)] println!(" Hull edge {hull_edge} (circumcenter {circumcenter}) extended orthogonally to {a} -> {b} at {}", v);
v
}
fn is_vertex_inside_bounding_box(&self, vertex: usize) -> bool {
*self.is_vertex_inside_bounding_box.get(vertex).unwrap_or(&false)
}
fn add_new_vertex(&mut self, vertex: Point) -> usize {
for (index, v) in self.vertices.iter().enumerate().skip(self.number_of_circumcenters) {
if utils::abs_diff_eq(v.x, vertex.x, utils::EQ_EPSILON)
&& utils::abs_diff_eq(v.y, vertex.y, utils::EQ_EPSILON) {
return index;
}
}
let index = self.vertices.len();
self.vertices.push(vertex);
index
}
}
fn calculate_corner_ownership(corners: &[Point], triangulation: &Triangulation, sites: &Vec<Point>, site_to_incoming_leftmost_halfedge: &Vec<usize>) -> Vec<usize> {
let mut corner_owners: Vec<usize> = Vec::with_capacity(corners.len());
let mut site = *triangulation.hull.first().expect("Hull is at least a triangle.");
for corner in corners {
let owner = crate::iterator::shortest_path_iter_from_triangulation(
triangulation,
sites,
site_to_incoming_leftmost_halfedge,
site,
corner.clone())
.last()
.expect("There must be one site that is the closest.");
site = owner;
corner_owners.push(owner);
}
corner_owners
}
fn calculate_incoming_edges(triangulation: &Triangulation, num_of_sites: usize) -> Vec<usize> {
let mut site_to_incoming_leftmost_halfedge = vec![EMPTY; num_of_sites];
for e in 0..triangulation.triangles.len() {
let s = site_of_incoming(&triangulation, e);
if site_to_incoming_leftmost_halfedge[s] == EMPTY || triangulation.halfedges[e] == EMPTY {
site_to_incoming_leftmost_halfedge[s] = e;
}
}
debug_assert!(!site_to_incoming_leftmost_halfedge.iter().any(|e| *e == EMPTY), "One or more sites is not reacheable in the triangulation mesh. This usually indicate coincident points.");
site_to_incoming_leftmost_halfedge
}