use std::fs::File;
use std::io::Write;
use crate::prelude::*;
pub type Vector2f = nalgebra::Vector2<f32>;
const TOL: f32 = 1e-10;
#[derive(Debug)]
pub struct Vertex {
pos: Vector2f,
ccw_neib: usize,
edge_id: i32,
}
impl Vertex {
pub fn get_id(&self) -> i32 {
self.edge_id
}
pub fn get_pos(&self) -> Vector2f {
self.pos
}
pub fn get_pos_3d(&self) -> Vector3f {
Vector3f::new(self.pos.x, self.pos.y, 0.0)
}
}
struct CuttingLine {
pos: Vector2f,
r2: f32,
}
impl CuttingLine {
fn new(point: &Vector2f) -> Self {
let pos = 0.5*point;
let r2 = pos.norm_squared();
Self {pos, r2}
}
}
pub struct VoronoiCell {
vert: Vec<Vertex>,
init_vert: usize,
}
#[derive(Default,Debug)]
struct EdgeCut {
in_ind: usize,
in_dist: f32,
out_ind: usize,
out_dist: f32,
}
impl VoronoiCell {
pub fn new(xmin: f32, xmax: f32, ymin: f32, ymax: f32) -> Self {
let mut ret = Self {
vert: Vec::with_capacity(8),
init_vert: 0,
};
ret.vert.push(Vertex{pos: Vector2f::new(xmin, ymin), ccw_neib: 1, edge_id: -1});
ret.vert.push(Vertex{pos: Vector2f::new(xmax, ymin), ccw_neib: 2, edge_id: -2});
ret.vert.push(Vertex{pos: Vector2f::new(xmax, ymax), ccw_neib: 3, edge_id: -3});
ret.vert.push(Vertex{pos: Vector2f::new(xmin, ymax), ccw_neib: 0, edge_id: -4});
ret
}
#[inline(always)]
fn dist(&self, i: usize, line: &CuttingLine) -> f32 {
line.pos.dot(&self.pos(i))-line.r2
}
#[inline(always)]
fn next(&self, i: usize) -> usize {
unsafe{ self.vert.get_unchecked(i).ccw_neib }
}
#[inline(always)]
fn pos(&self, i: usize) -> &Vector2f {
unsafe{ &self.vert.get_unchecked(i).pos }
}
#[inline(always)]
fn vertex(&self, i: usize) -> &Vertex {
unsafe{ self.vert.get_unchecked(i) }
}
#[inline(always)]
fn vertex_mut(&mut self, i: usize) -> &mut Vertex {
unsafe{ self.vert.get_unchecked_mut(i) }
}
pub fn add_point(&mut self,point: &Vector2f, id: usize) -> bool {
let line = CuttingLine::new(point);
let cut1;
let cut2;
let mut cur_i = self.init_vert; let mut cur_d = self.dist(cur_i, &line);
while cur_d >= TOL {
cur_i = self.next(cur_i);
cur_d = self.dist(cur_i, &line)
}
self.init_vert = cur_i;
loop {
let next_i = self.next(cur_i);
if next_i==self.init_vert {
return false;
}
let next_d = self.dist(next_i, &line);
if next_d >= TOL {
cut1 = EdgeCut {
in_ind: cur_i,
in_dist: cur_d,
out_ind: next_i,
out_dist: next_d
};
cur_i = next_i;
cur_d = next_d;
break;
}
cur_i = next_i;
cur_d = next_d;
}
loop {
let next_i = self.next(cur_i);
let next_d = self.dist(next_i, &line);
if next_d < TOL {
cut2 = EdgeCut {
out_ind: cur_i,
out_dist: cur_d,
in_ind: next_i,
in_dist: next_d
};
break;
}
cur_i = next_i;
cur_d = next_d;
}
let p = self.vertex_pos_from_cut(&cut2);
if cut1.out_ind != cut2.out_ind {
self.vertex_mut(cut2.out_ind).pos = p;
self.vertex_mut(cut1.out_ind).ccw_neib = cut2.out_ind;
} else {
let old_id = self.vertex(cut2.out_ind).edge_id;
self.vert.push(
Vertex {
pos: p,
ccw_neib: cut2.in_ind, edge_id: old_id }
);
self.vertex_mut(cut1.out_ind).ccw_neib = self.vert.len()-1; }
self.vertex_mut(cut1.out_ind).pos = self.vertex_pos_from_cut(&cut1);
self.vertex_mut(cut1.out_ind).edge_id = id as i32;
true
}
#[inline(always)]
fn vertex_pos_from_cut(&self, cut: &EdgeCut) -> Vector2f {
let frac = cut.out_dist / (cut.in_dist.abs()+cut.out_dist);
(1.0-frac)*self.pos(cut.out_ind)+frac*self.pos(cut.in_ind)
}
pub fn iter_vertex(&self) -> VoronoiCellVertexIter<'_> {
VoronoiCellVertexIter{
cell: self,
cur: self.init_vert,
stop: false
}
}
pub fn write_to_file(&self, fname: &str) {
let mut s = String::new();
self.for_each_vert(
|v| s.push_str(&format!("{} {}\n",v.pos[0],v.pos[1]))
);
let mut out = File::create(fname).unwrap();
write!(out,"{}",s).unwrap();
}
pub fn area(&self) -> f32 {
let mut a = 0.0;
let mut i = self.init_vert;
loop {
let v1 = self.vertex(i);
i = v1.ccw_neib;
let v2 = self.vertex(i);
a += (v1.pos.x * v2.pos.y - v1.pos.y * v2.pos.x).abs() / 2.0;
if i == self.init_vert {break}
}
a
}
pub fn for_each_vert(&self, mut func: impl FnMut(&Vertex)) {
let mut i = self.init_vert;
loop {
let v = self.vertex(i);
func(v);
i = v.ccw_neib;
if i == self.init_vert {break}
}
}
}
pub struct VoronoiCellVertexIter<'a> {
cell: &'a VoronoiCell,
cur: usize,
stop: bool,
}
impl<'a> Iterator for VoronoiCellVertexIter<'a> {
type Item = &'a Vertex;
fn next(&mut self) -> Option<Self::Item> {
if self.stop {
None
} else {
let ret = &self.cell.vert[self.cur];
self.cur = ret.ccw_neib;
if self.cur == self.cell.init_vert {
self.stop = true;
}
Some(ret)
}
}
}
#[cfg(test)]
mod tests {
use super::{Vector2f, VoronoiCell};
#[test]
fn cell_test() {
let mut c = VoronoiCell::new(-10.0, 10.0, -10.0, 10.0);
c.add_point(&Vector2f::new(20.0,-17.5), 1);
c.add_point(&Vector2f::new(10.0,0.0), 2);
c.add_point(&Vector2f::new(10.0,10.0), 3);
c.add_point(&Vector2f::new(-5.0*2.0,-2.0*2.0), 4);
c.add_point(&Vector2f::new(-5.0*2.0,5.0*2.0), 5);
c.write_to_file("../target/out.dat");
for v in c.iter_vertex() {
println!("{v:?}")
}
}
}