use super::demo_data;
use crate::cast;
use crate::{ColorFlag, DrawFilterFlag, Example, FH, FW, SharedData};
use boostvoronoi::prelude::*;
use boostvoronoi::try_cast;
use boostvoronoi::utils::visual_utils::{Aabb2, SimpleAffine};
use fltk::{dialog, draw, enums};
use geo::Intersects;
use num_traits::AsPrimitive;
pub struct VoronoiVisualizer<I: InputType> {
screen_aabb: Aabb2,
pub(crate) diagram: Diagram,
points_aabb: Aabb2,
pub(crate) point_data_: Vec<Point<I>>,
pub(crate) segment_data_: Vec<Line<I>>,
pub(crate) affine: SimpleAffine,
}
impl<I: InputType> Default for VoronoiVisualizer<I> {
fn default() -> Self {
Self {
screen_aabb: Aabb2::new_from_i32::<I>(0, 0, FW, FH),
diagram: Diagram::default(),
points_aabb: Aabb2::default(),
point_data_: Vec::<Point<I>>::new(),
segment_data_: Vec::<Line<I>>::new(),
affine: SimpleAffine::default(),
}
}
}
impl<I: InputType> VoronoiVisualizer<I>
where
Line<I>: From<[i32; 4]>,
Point<I>: From<[i32; 2]>,
i32: AsPrimitive<I>,
i64: AsPrimitive<I>,
{
pub fn re_calculate_affine(&mut self) -> Result<(), BvError> {
self.affine = SimpleAffine::new::<I>(&self.points_aabb, &self.screen_aabb)?;
self.affine.scale[1] *= -1.0;
Ok(())
}
pub fn build(&mut self) -> Result<String, BvError> {
if false {
println!(
"Running voronoi with this input (in case of a crash, copy&paste and make a test case)"
);
print!(" let points:[[i32;2];{}]=[", self.point_data_.len());
for p in self.point_data_.iter() {
print!("[{},{}],", p.x, p.y)
}
println!("];");
print!(" let segments:[[i32;4];{}]=[", self.segment_data_.len());
for s in self.segment_data_.iter() {
print!("[{},{},{},{}],", s.start.x, s.start.y, s.end.x, s.end.y)
}
println!("];");
}
if false {
print!(" int INPUT_PTS[{}][2]={{", self.point_data_.len());
for p in self.point_data_.iter() {
print!("{{{},{}}},", p.x, p.y)
}
println!("}};");
print!(" int INPUT_SGS[{}][4]={{", self.segment_data_.len());
for s in self.segment_data_.iter() {
print!("{{{},{},{},{}}},", s.start.x, s.start.y, s.end.x, s.end.y)
}
println!("}};");
}
self.diagram = Builder::<I>::default()
.with_vertices(self.point_data_.iter())?
.with_segments(self.segment_data_.iter())?
.build()?;
println!("Result: found {} vertices", self.diagram.vertices().len());
self.points_aabb = {
let mut aabb = Aabb2::default();
for p in self.point_data_.iter() {
aabb.update_point(p);
}
for l in self.segment_data_.iter() {
aabb.update_line(l);
}
aabb.grow_percent::<I>(20);
aabb
};
self.diagram
.color_exterior_edges(ColorFlag::EXTERNAL.bits());
for edge_idx in self.diagram.decoupled_edges_iter() {
let should_mark = {
self.diagram.edge_is_infinite(edge_idx)? || {
let twin = self.diagram.edge_get_twin(edge_idx)?;
self.diagram.edge_is_infinite(twin)?
}
};
if should_mark {
self.diagram
.edge_or_color(edge_idx, ColorFlag::INFINITE.bits())?;
}
}
Result::Ok("".to_string())
}
pub(crate) fn self_intersecting_check(&self, l: &Line<I>) -> bool {
let l_ = Self::line_i_to_f64(l);
for s in self.segment_data_.iter() {
if (s.start.x == l.start.x && s.start.y == l.start.y)
&& (s.end.x != l.end.x && s.end.y != l.end.y)
{
continue;
}
if (s.start.x == l.end.x && s.start.y == l.end.y)
&& (s.end.x != l.start.x && s.end.y != l.start.y)
{
continue;
}
if (s.end.x == l.end.x && s.end.y == l.end.y)
&& (s.start.x != l.start.x && s.start.y != l.start.y)
{
continue;
}
if (s.end.x == l.start.x && s.end.y == l.start.y)
&& (s.start.x != l.end.x && s.start.y != l.end.y)
{
continue;
}
let s_ = Self::line_i_to_f64(s);
if l_.intersects(&s_) {
return true;
}
}
false
}
pub(crate) fn draw(&self, config: &SharedData) -> Result<(), BvError> {
draw::set_line_style(draw::LineStyle::Solid, 1);
draw::set_draw_color(enums::Color::Red);
if config.draw_flag.contains(DrawFilterFlag::INPUT_POINT) {
self.draw_input_points(&self.affine);
}
if config.draw_flag.contains(DrawFilterFlag::INPUT_SEGMENT) {
self.draw_input_segments(&self.affine);
}
if config.draw_flag.contains(DrawFilterFlag::EDGES) {
draw::set_draw_color(enums::Color::Green);
self.draw_edges(config, &self.affine)?;
}
if config.draw_flag.contains(DrawFilterFlag::VERTICES) {
draw::set_draw_color(enums::Color::Blue);
self.draw_vertices(config, &self.affine);
}
Ok(())
}
fn draw_input_points(&self, affine: &SimpleAffine) {
let draw = |point: [f64; 2]| {
draw::draw_circle(point[0], point[1], 2.0);
};
for i in self.point_data_.iter() {
draw(affine.transform_p(i));
}
for i in self.segment_data_.iter() {
draw(affine.transform_p(&i.start));
draw(affine.transform_p(&i.end));
}
}
fn draw_input_segments(&self, affine: &SimpleAffine) {
for i in self.segment_data_.iter() {
let sp = affine.transform_p(&i.start);
let ep = affine.transform_p(&i.end);
draw::draw_line(sp[0].as_(), sp[1].as_(), ep[0].as_(), ep[1].as_());
}
}
fn draw_vertices(&self, config: &SharedData, affine: &SimpleAffine) {
let draw = |x: f64, y: f64| {
draw::draw_circle(x, y, 1.0);
};
let draw_external = config.draw_flag.contains(DrawFilterFlag::EXTERNAL);
let draw_site_vertex = config.draw_flag.contains(DrawFilterFlag::SITE_VERTEX);
for it in self.diagram.vertices().iter().enumerate() {
let vertex = it.1;
if (!draw_site_vertex) && vertex.is_site_point() {
continue;
}
if (!draw_external)
&& ColorFlag::from_bits(vertex.get_color())
.unwrap()
.contains(ColorFlag::EXTERNAL)
{
continue;
}
draw(
affine.transform_x(vertex.x()),
affine.transform_y(vertex.y()),
);
}
}
fn draw_edges(&self, config: &SharedData, affine: &SimpleAffine) -> Result<(), BvError> {
let draw_external = config.draw_flag.contains(DrawFilterFlag::EXTERNAL);
let draw_primary = config.draw_flag.contains(DrawFilterFlag::PRIMARY);
let draw_secondary = config.draw_flag.contains(DrawFilterFlag::SECONDARY);
let draw_curved = config.draw_flag.contains(DrawFilterFlag::CURVE);
let draw_curved_as_line = config.draw_flag.contains(DrawFilterFlag::CURVE_LINE);
let draw_infinite_edges = config.draw_flag.contains(DrawFilterFlag::INFINITE);
let mut already_drawn = {
let len = self.diagram.edges().len();
let mut vb = vob::Vob::<u32>::new_with_storage_type(len);
vb.resize(len, false);
vb
};
for it in self.diagram.edges().iter() {
draw::set_draw_color(enums::Color::DarkGreen);
let edge = it;
let edge_id = edge.id();
if already_drawn.get(edge_id.usize()).unwrap_or(false) {
continue;
}
let twin = self.diagram.edge_get_twin(edge_id)?;
if twin > edge_id {
already_drawn.set(twin.usize(), true);
}
if (!draw_primary) && edge.is_primary() {
continue;
}
if edge.is_secondary() && (!draw_secondary) {
continue;
}
if ColorFlag::from_bits(edge.get_color())
.unwrap()
.contains(ColorFlag::INFINITE)
{
if !draw_infinite_edges {
continue;
} else {
draw::set_draw_color(enums::Color::Green);
}
}
if ColorFlag::from_bits(edge.get_color())
.unwrap()
.contains(ColorFlag::EXTERNAL)
{
if !draw_external {
continue;
} else {
draw::set_draw_color(enums::Color::Green);
}
}
let mut samples = Vec::<[f64; 2]>::new();
if self.diagram.edge_is_infinite(edge_id)? {
let a = self.clip_infinite_edge(affine, edge_id, &mut samples);
if let Err(err) = a {
println!("Ignoring error : {:?}", err);
}
} else {
let vertex0 = self.diagram.vertex(edge.vertex0().unwrap())?;
samples.push(affine.transform(vertex0.x(), vertex0.y()));
let vertex1 = self.diagram.edge_get_vertex1(edge_id)?.unwrap();
let vertex1 = self.diagram.vertex(vertex1).unwrap();
samples.push(affine.transform(vertex1.x(), vertex1.y()));
if edge.is_curved() {
if draw_curved_as_line {
for i in 0..samples.len() - 1 {
if let Ok(x1) = try_cast::<f64, i32>(samples[i][0]) {
if let Ok(y1) = try_cast::<f64, i32>(samples[i][1]) {
if let Ok(x2) = try_cast::<f64, i32>(samples[i + 1][0]) {
if let Ok(y2) = try_cast::<f64, i32>(samples[i + 1][1]) {
draw::draw_line(x1, y1, x2, y2);
}
}
}
}
}
}
if draw_curved {
self.sample_curved_edge(affine, edge_id, &mut samples)?;
} else {
continue;
}
}
}
if samples.len() > 1 {
for i in 0..samples.len() - 1 {
let x1 = try_cast::<f64, i32>(samples[i][0]);
if x1.is_err() {
break;
}
let y1 = try_cast::<f64, i32>(samples[i][1]);
if y1.is_err() {
break;
}
let x2 = try_cast::<f64, i32>(samples[i + 1][0]);
if x2.is_err() {
break;
}
let y2 = try_cast::<f64, i32>(samples[i + 1][1]);
if y2.is_err() {
break;
}
draw::draw_line(x1.unwrap(), y1.unwrap(), x2.unwrap(), y2.unwrap());
}
}
}
Ok(())
}
fn clip_infinite_edge(
&self,
affine: &SimpleAffine,
edge_id: EdgeIndex,
clipped_edge: &mut Vec<[f64; 2]>,
) -> Result<(), BvError> {
let edge = self.diagram.edge(edge_id)?;
let cell1_id = self.diagram.edge_get_cell(edge_id)?;
let cell1 = self.diagram.cell(cell1_id)?;
let cell2_id = {
let twin = self.diagram.edge_get_twin(edge_id)?;
self.diagram.edge_get_cell(twin)?
};
let cell2 = self.diagram.cell(cell2_id)?;
let mut origin = [0.0_f64, 0.0];
let mut direction = [0.0_f64, 0.0];
if cell1.contains_point() && cell2.contains_point() {
let p1 = self.retrieve_point(cell1_id)?;
let p2 = self.retrieve_point(cell2_id)?;
origin[0] = (cast::<I, f64>(p1.x) + cast::<I, f64>(p2.x)) * 0.5;
origin[1] = (cast::<I, f64>(p1.y) + cast::<I, f64>(p2.y)) * 0.5;
direction[0] = cast::<I, f64>(p1.y) - cast::<I, f64>(p2.y);
direction[1] = cast::<I, f64>(p2.x) - cast::<I, f64>(p1.x);
} else {
origin = if cell1.contains_segment() {
let p = self.retrieve_point(cell2_id)?;
[(p.x.as_()), (p.y.as_())]
} else {
let p = self.retrieve_point(cell1_id)?;
[(p.x.as_()), (p.y.as_())]
};
let segment = if cell1.contains_segment() {
self.retrieve_segment(cell1_id)?
} else {
self.retrieve_segment(cell2_id)?
};
let dx = segment.end.x - segment.start.x;
let dy = segment.end.y - segment.start.y;
if ([cast::<I, f64>(segment.start.x), segment.start.y.as_()] == origin)
^ cell1.contains_point()
{
direction[0] = dy.as_();
direction[1] = (-dx).as_();
} else {
direction[0] = (-dy).as_();
direction[1] = dx.as_();
}
}
let side: f64 = {
let high = affine.reverse_transform_fx(self.screen_aabb.get_high().unwrap()[0]);
let low = affine.reverse_transform_fx(self.screen_aabb.get_low().unwrap()[0]);
(high - low).abs()
};
let coefficient = side / (direction[0].abs().max(direction[1].abs()));
if let Some(vertex0) = edge.vertex0() {
let vertex0 = self.diagram.vertex(vertex0)?;
clipped_edge.push([
affine.transform_x(vertex0.x()),
affine.transform_y(vertex0.y()),
]);
} else {
clipped_edge.push([
affine.transform_x(origin[0] - direction[0] * coefficient),
affine.transform_y(origin[1] - direction[1] * coefficient),
]);
}
if let Some(vertex1) = self.diagram.edge_get_vertex1(edge_id)? {
let vertex1 = self.diagram.vertex(vertex1)?;
clipped_edge.push([
affine.transform_x(vertex1.x()),
affine.transform_y(vertex1.y()),
]);
} else {
clipped_edge.push([
affine.transform_x(origin[0] + direction[0] * coefficient),
affine.transform_y(origin[1] + direction[1] * coefficient),
]);
}
Ok(())
}
fn sample_curved_edge(
&self,
affine: &SimpleAffine,
edge_id: EdgeIndex,
sampled_edge: &mut Vec<[f64; 2]>,
) -> Result<(), BvError> {
let max_dist = 1E-3
* (self.screen_aabb.get_high().unwrap()[0] - self.screen_aabb.get_low().unwrap()[0]);
let cell_id = self.diagram.edge_get_cell(edge_id)?;
let cell = self.diagram.cell(cell_id)?;
let twin_id = self.diagram.edge_get_twin(edge_id)?;
let twin_cell_id = self.diagram.edge_get_cell(twin_id)?;
let point = if cell.contains_point() {
self.retrieve_point(cell_id)?
} else {
self.retrieve_point(twin_cell_id)?
};
let segment = if cell.contains_point() {
self.retrieve_segment(twin_cell_id)?
} else {
self.retrieve_segment(cell_id)?
};
VoronoiVisualUtils::discretize::<I>(&point, segment, max_dist, affine, sampled_edge);
Ok(())
}
fn retrieve_point(&self, cell_id: CellIndex) -> Result<Point<I>, BvError> {
let (index, cat) = self.diagram.cell(cell_id)?.source_index_2();
match cat {
SourceCategory::SinglePoint => {
let idx = index.usize();
if self.point_data_.len() < idx {
return Err(BvError::ValueError(
format!(
"point_data_.len():{:?} < index:{:?}",
self.point_data_.len(),
index
)
.to_string(),
));
}
Ok(self.point_data_[idx])
}
SourceCategory::SegmentStart => {
let iindex: usize = index.usize();
if iindex < self.point_data_.len() {
return Err(BvError::ValueError(
format!("index out of range :{index:?} {}", self.point_data_.len())
.to_string(),
));
}
Ok(self.segment_data_[index.usize() - self.point_data_.len()].start)
}
SourceCategory::Segment | SourceCategory::SegmentEnd => {
let iindex = index.usize();
if iindex < self.point_data_.len() {
return Err(BvError::ValueError(
format!("index out of range :{index:?} {}", self.point_data_.len())
.to_string(),
));
}
Ok(self.segment_data_[iindex - self.point_data_.len()].end)
}
}
}
fn retrieve_segment(&self, cell_id: CellIndex) -> Result<&Line<I>, BvError> {
let cell = self.diagram.cell(cell_id)?;
let source_index: usize = cell.source_index().usize();
if source_index < self.point_data_.len() {
return Err(BvError::ValueError(
format!(
"cell.source_index() out of range :{source_index} {} contains segment:{}",
self.point_data_.len(),
cell.contains_segment()
)
.to_string(),
));
}
Ok(&self.segment_data_[source_index - self.point_data_.len()])
}
#[allow(unused_assignments)]
pub(crate) fn read_data(&mut self, example: Example) -> String {
self.segment_data_.clear();
self.point_data_.clear();
self.diagram.clear();
let mut rv = String::new();
let (new_points, new_segments) = match example {
Example::Simple => {
rv = "Simple example".to_string();
(
demo_data::NO_POINTS
.into_iter()
.map(|x| x.cast::<I>())
.collect(),
demo_data::SIMPLE_SEGMENTS
.into_iter()
.map(|l| Line::<i32>::from(l).cast::<I>())
.collect(),
)
}
Example::Complex => {
rv = "Rust logo".to_string();
(
demo_data::NO_POINTS
.into_iter()
.map(|x| x.cast::<I>())
.collect(),
demo_data::COMPLEX_SEGMENTS
.into_iter()
.map(|l| Line::<i32>::from(l))
.map(|x| x.cast::<I>())
.collect(),
)
}
Example::Clean => {
rv = "Clean".to_string();
(
demo_data::NO_POINTS
.into_iter()
.map(|x| x.cast::<I>())
.collect(),
demo_data::NO_SEGMENTS
.into_iter()
.map(|x| x.cast::<I>())
.collect(),
)
}
Example::File => {
let mut chooser = dialog::NativeFileChooser::new(dialog::FileDialogType::BrowseDir);
let _ = chooser.set_directory(&std::path::PathBuf::from("examples"));
chooser.set_title("select your input data");
chooser.set_filter("*.txt");
chooser.show();
if let Some(filename) = chooser.filenames().first() {
if let Ok(file_parse_result) = read_boost_input_file::<I>(filename.as_path()) {
rv = filename.to_str().unwrap().to_string();
file_parse_result
} else {
rv = "Failed to read file".to_string();
(
demo_data::NO_POINTS
.into_iter()
.map(|x| x.cast::<I>())
.collect(),
demo_data::NO_SEGMENTS
.into_iter()
.map(|x| x.cast::<I>())
.collect(),
)
}
} else {
rv = "Failed to read file".to_string();
(
demo_data::NO_POINTS
.into_iter()
.map(|x| x.cast::<I>())
.collect(),
demo_data::NO_SEGMENTS
.into_iter()
.map(|x| x.cast::<I>())
.collect(),
)
}
}
};
self.point_data_ = new_points;
self.segment_data_ = new_segments;
rv
}
#[inline(always)]
fn line_i_to_f64(value: &Line<I>) -> geo::Line<f64> {
let ps = geo::Coord {
x: (value.start.x.as_()),
y: (value.start.y.as_()),
};
let pe = geo::Coord {
x: (value.end.x.as_()),
y: (value.end.y.as_()),
};
geo::Line::<f64>::new(ps, pe)
}
}