use crate::{
BvError, InputType, cast,
geometry::{Line, Point},
};
use num_traits::AsPrimitive;
use ordered_float::NotNan;
use std::fmt;
pub struct VoronoiVisualUtils {}
impl VoronoiVisualUtils {
pub fn discretize<I: InputType>(
point: &Point<I>,
segment: &Line<I>,
max_dist: f64,
affine: &SimpleAffine,
discretization: &mut Vec<[f64; 2]>,
) {
if discretization[0][0] == discretization[1][0]
&& discretization[0][1] == discretization[1][1]
{
return;
}
let segm_vec_x: f64 =
affine.transform_ix(segment.end.x) - affine.transform_ix(segment.start.x);
let segm_vec_y: f64 =
affine.transform_iy(segment.end.y) - affine.transform_iy(segment.start.y);
let sqr_segment_length = segm_vec_x * segm_vec_x + segm_vec_y * segm_vec_y;
let projection_start =
sqr_segment_length * Self::point_projection(affine, &discretization[0], segment);
let projection_end =
sqr_segment_length * Self::point_projection(affine, &discretization[1], segment);
let point_vec_x = affine.transform_ix(point.x) - affine.transform_ix(segment.start.x);
let point_vec_y = affine.transform_iy(point.y) - affine.transform_iy(segment.start.y);
let rot_x = segm_vec_x * point_vec_x + segm_vec_y * point_vec_y;
let rot_y = segm_vec_x * point_vec_y - segm_vec_y * point_vec_x;
let last_point = (*discretization)[1];
let _ = discretization.pop();
let mut point_stack = vec![projection_end];
let mut cur_x = projection_start;
let mut cur_y = Self::parabola_y(cur_x, rot_x, rot_y);
let max_dist_transformed = max_dist * max_dist * sqr_segment_length;
while !point_stack.is_empty() {
let new_x = point_stack[point_stack.len() - 1]; let new_y = Self::parabola_y(new_x, rot_x, rot_y);
let mid_x = (new_y - cur_y) / (new_x - cur_x) * rot_y + rot_x;
let mid_y = Self::parabola_y(mid_x, rot_x, rot_y);
let mut dist = (new_y - cur_y) * (mid_x - cur_x) - (new_x - cur_x) * (mid_y - cur_y);
#[allow(clippy::suspicious_operation_groupings)]
{
dist = dist * dist
/ ((new_y - cur_y) * (new_y - cur_y) + (new_x - cur_x) * (new_x - cur_x));
}
if dist.is_nan() {
break;
}
if dist <= max_dist_transformed {
let _ = point_stack.pop();
let inter_x = (segm_vec_x * new_x - segm_vec_y * new_y) / sqr_segment_length
+ affine.transform_ix(segment.start.x);
let inter_y = (segm_vec_x * new_y + segm_vec_y * new_x) / sqr_segment_length
+ affine.transform_iy(segment.start.y);
discretization.push([inter_x, inter_y]);
cur_x = new_x;
cur_y = new_y;
} else {
point_stack.push(mid_x);
}
}
let discretization_len = discretization.len();
discretization[discretization_len - 1] = last_point;
}
#[inline(always)]
#[allow(clippy::suspicious_operation_groupings)]
fn parabola_y(x: f64, a: f64, b: f64) -> f64 {
((x - a) * (x - a) + b * b) / (b + b)
}
pub fn point_projection<I: InputType>(
affine: &SimpleAffine,
point: &[f64; 2],
segment: &Line<I>,
) -> f64 {
let segment_vec_x: f64 =
affine.transform_ix(segment.end.x) - affine.transform_ix(segment.start.x);
let segment_vec_y: f64 =
affine.transform_iy(segment.end.y) - affine.transform_iy(segment.start.y);
let point_vec_x = point[0] - affine.transform_ix(segment.start.x);
let point_vec_y = point[1] - affine.transform_iy(segment.start.y);
let sqr_segment_length = segment_vec_x * segment_vec_x + segment_vec_y * segment_vec_y;
let vec_dot = segment_vec_x * point_vec_x + segment_vec_y * point_vec_y;
vec_dot / sqr_segment_length
}
}
type AabbInnerType = ([NotNan<f64>; 2], [NotNan<f64>; 2]);
#[derive(PartialEq, Eq, Clone, fmt::Debug)]
pub struct Aabb2 {
min_max_: Option<AabbInnerType>,
}
impl Default for Aabb2 {
#[inline]
fn default() -> Self {
Self { min_max_: None }
}
}
impl Aabb2 {
pub fn new<I: InputType>(p1: &Point<I>, p2: &Point<I>) -> Self {
let mut rv = Self::default();
rv.update_point(p1);
rv.update_point(p2);
rv
}
pub fn new_from_i32<I: InputType>(x1: i32, y1: i32, x2: i32, y2: i32) -> Self {
let mut rv = Self::default();
rv.update_coordinate::<I>(x1, y1);
rv.update_coordinate::<I>(x2, y2);
rv
}
#[inline(always)]
pub fn update_point<I: InputType>(&mut self, point: &Point<I>) {
let x = cast::<I, f64>(point.x);
let y = cast::<I, f64>(point.y);
self.update_vertex(x, y);
}
#[inline(always)]
pub fn update_coordinate<I: InputType>(&mut self, x: i32, y: i32) {
self.update_vertex(x.as_(), y.as_());
}
#[inline]
pub fn update_vertex(&mut self, x: f64, y: f64) {
let x = NotNan::new(x).unwrap();
let y = NotNan::new(y).unwrap();
if self.min_max_.is_none() {
self.min_max_ = Some(([x, y], [x, y]));
return;
}
let (mut aabb_min, mut aabb_max) = self.min_max_.take().unwrap();
if x < aabb_min[0] {
aabb_min[0] = x;
}
if y < aabb_min[1] {
aabb_min[1] = y;
}
if x > aabb_max[0] {
aabb_max[0] = x;
}
if y > aabb_max[1] {
aabb_max[1] = y;
}
self.min_max_ = Some((aabb_min, aabb_max));
}
#[inline]
pub fn update_i64(&mut self, x: i64, y: i64) {
self.update_vertex(x.as_(), y.as_())
}
#[inline]
pub fn update_f64(&mut self, x: f64, y: f64) {
self.update_vertex(x, y)
}
#[inline(always)]
pub fn update_line<I: InputType>(&mut self, line: &Line<I>) {
self.update_point(&line.start);
self.update_point(&line.end);
}
#[inline(always)]
pub fn get_high(&self) -> Option<[f64; 2]> {
if let Some((_, high)) = self.min_max_ {
return Some([high[0].into_inner(), high[1].into_inner()]);
}
None
}
#[inline(always)]
pub fn get_low(&self) -> Option<[f64; 2]> {
if let Some((low, _)) = self.min_max_ {
return Some([low[0].into_inner(), low[1].into_inner()]);
}
None
}
pub fn grow_percent<I: InputType>(&mut self, percent: i32) {
if self.min_max_.is_some() {
let size_x = self.get_high().unwrap()[0] - self.get_low().unwrap()[0];
let size_y = self.get_high().unwrap()[1] - self.get_low().unwrap()[1];
let size = if size_x > size_y { size_x } else { size_y };
let delta = size * ((percent as f64) / 100.0);
let mut p = self.get_high().unwrap();
p[0] += delta;
p[1] += delta;
self.update_vertex(p[0], p[1]);
let mut p = self.get_low().unwrap();
p[0] -= delta;
p[1] -= delta;
self.update_vertex(p[0], p[1]);
}
}
#[inline]
pub fn contains_point<I: InputType>(&self, point: &Point<I>) -> Option<bool> {
if let Some(min_max) = self.min_max_ {
let x: f64 = point.x.as_();
let y: f64 = point.y.as_();
Some(
x >= min_max.0[0].into_inner()
&& x <= min_max.1[0].into_inner()
&& y >= min_max.0[1].into_inner()
&& y <= min_max.1[1].into_inner(),
)
} else {
None
}
}
#[inline]
pub fn contains_line<I: InputType>(&self, line: &Line<I>) -> Option<bool> {
if self.min_max_.is_some() {
Some(
self.contains_point(&line.start).unwrap()
&& self.contains_point(&line.end).unwrap(),
)
} else {
None
}
}
}
#[derive(PartialEq, Clone, fmt::Debug)]
pub struct SimpleAffine {
to_center_: [f64; 2],
pub scale: [f64; 2],
pub to_offset: [f64; 2],
}
impl Default for SimpleAffine {
#[inline]
fn default() -> Self {
Self {
to_center_: [0.0, 0.0],
scale: [1.0, 1.0],
to_offset: [0., 0.0],
}
}
}
impl SimpleAffine {
pub fn new<I: InputType>(source_aabb: &Aabb2, dest_aabb: &Aabb2) -> Result<Self, BvError> {
let min_dim = 10.0;
if let Some(s_low) = source_aabb.get_low() {
if let Some(s_high) = source_aabb.get_high() {
if let Some(d_low) = dest_aabb.get_low() {
if let Some(d_high) = dest_aabb.get_high() {
let source_aabb_center =
[-(s_low[0] + s_high[0]) / 2.0, -(s_low[1] + s_high[1]) / 2.0];
let source_aabb_size = [
(s_high[0] - s_low[0]).max(min_dim),
(s_high[1] - s_low[1]).max(min_dim),
];
let dest_aabb_center =
[(d_low[0] + d_high[0]) / 2.0, (d_low[1] + d_high[1]) / 2.0];
let dest_aabb_size = [
(d_high[0] - d_low[0]).max(min_dim),
(d_high[1] - d_low[1]).max(min_dim),
];
let source_aabb_size = source_aabb_size[0].max(source_aabb_size[1]);
let dest_aabb_size = dest_aabb_size[0].min(dest_aabb_size[1]);
let scale = dest_aabb_size / source_aabb_size;
return Ok(Self {
to_center_: source_aabb_center,
scale: [scale, scale],
to_offset: dest_aabb_center,
});
}
}
}
}
Err(BvError::InternalError(format!(
"could not get dimension of the AABB. {}:{}",
file!(),
line!()
)))
}
#[inline(always)]
pub fn reverse_transform<I: InputType>(&self, x: f64, y: f64) -> Result<[I; 2], BvError> {
let x = self.reverse_transform_x(x)?;
let y = self.reverse_transform_y(y)?;
Ok([x, y])
}
#[inline(always)]
pub fn reverse_transform_x<I: InputType>(&self, x: f64) -> Result<I, BvError> {
crate::try_cast::<f64, I>(
((x - self.to_offset[0]) / self.scale[0] - self.to_center_[0]).round(),
)
}
#[inline(always)]
pub fn reverse_transform_y<I: InputType>(&self, y: f64) -> Result<I, BvError> {
crate::try_cast::<f64, I>(
((y - self.to_offset[1]) / self.scale[1] - self.to_center_[1]).round(),
)
}
#[inline(always)]
pub fn reverse_transform_fx(&self, x: f64) -> f64 {
((x - self.to_offset[0]) / self.scale[0] - self.to_center_[0]).round()
}
#[inline(always)]
pub fn reverse_transform_fy(&self, y: f64) -> f64 {
((y - self.to_offset[1]) / self.scale[1] - self.to_center_[1]).round()
}
#[inline(always)]
pub fn transform(&self, x: f64, y: f64) -> [f64; 2] {
[self.transform_x(x), self.transform_y(y)]
}
#[inline(always)]
pub fn transform_x(&self, x: f64) -> f64 {
(x + self.to_center_[0]) * self.scale[0] + self.to_offset[0]
}
#[inline(always)]
pub fn transform_y(&self, y: f64) -> f64 {
(y + self.to_center_[1]) * self.scale[1] + self.to_offset[1]
}
#[inline(always)]
pub fn transform_i<I: InputType>(&self, point: [I; 2]) -> [f64; 2] {
[self.transform_ix(point[0]), self.transform_iy(point[1])]
}
#[inline(always)]
pub fn transform_f(&self, point: [f64; 2]) -> [f64; 2] {
[self.transform_fx(point[0]), self.transform_fy(point[1])]
}
#[inline(always)]
pub fn transform_p<I: InputType>(&self, point: &Point<I>) -> [f64; 2] {
[self.transform_ix(point.x), self.transform_iy(point.y)]
}
#[inline(always)]
pub fn transform_ix<I: InputType>(&self, x: I) -> f64 {
(cast::<I, f64>(x) + self.to_center_[0]) * self.scale[0] + self.to_offset[0]
}
#[inline(always)]
pub fn transform_iy<I: InputType>(&self, y: I) -> f64 {
(cast::<I, f64>(y) + self.to_center_[1]) * self.scale[1] + self.to_offset[1]
}
#[inline(always)]
pub fn transform_fx(&self, x: f64) -> f64 {
(x + self.to_center_[0]) * self.scale[0] + self.to_offset[0]
}
#[inline(always)]
pub fn transform_fy(&self, y: f64) -> f64 {
(y + self.to_center_[1]) * self.scale[1] + self.to_offset[1]
}
#[inline(always)]
pub fn zoom(&mut self, f: f64) {
self.scale = [self.scale[0] * f, self.scale[1] * f];
}
}
pub fn to_segments_offset<I1: InputType, I2: InputType>(
points: &[[I1; 4]],
scale_x: f64,
scale_y: f64,
dx: i64,
dy: i64,
) -> Vec<Line<I2>> {
let fx = |x: I1| cast::<f64, I2>(cast::<I1, f64>(x) * scale_x) + cast::<i64, I2>(dx);
let fy = |y: I1| cast::<f64, I2>(cast::<I1, f64>(y) * scale_y) + cast::<i64, I2>(dy);
points
.iter()
.map(|x| Line {
start: Point {
x: fx(x[0]),
y: fy(x[1]),
},
end: Point {
x: fx(x[2]),
y: fy(x[3]),
},
})
.collect()
}