use crate::{LinestringError, linestring_3d};
use std::fmt::Debug;
use vector_traits::{
approx::*,
num_traits::{AsPrimitive, Float, One, Zero},
prelude::{Plane, *},
};
pub mod convex_hull;
#[doc(hidden)]
pub mod impls;
pub mod indexed_intersection;
pub mod intersection;
pub(crate) mod simplify;
#[cfg(test)]
mod tests;
pub trait LineString2<T: GenericVector2> {
fn is_connected(&self) -> bool;
fn point_count(&self) -> usize;
fn is_self_intersecting(&self) -> Result<bool, LinestringError>;
fn intersection(&self, other: &Line2<T>) -> Option<Intersection<T>>;
fn find_two_intersections(&self, other: &Line2<T>) -> (Option<T>, Option<T>);
fn find_all_intersections(&self, other: &Line2<T>) -> Vec<Intersection<T>>;
fn ray_edge_intersection(origin: T, ray_dir: T, p0: T, p1: T) -> Option<T::Scalar>;
fn closest_ray_intersection(&self, ray_dir: T, origin: T) -> Option<T>;
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
fn window_iter(&self) -> WindowIterator<'_, T>;
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
fn chunk_iter(&self) -> ChunkIterator<'_, T>;
fn copy_to_3d(&self, plane: Plane) -> Vec<T::Vector3>;
fn simplify_rdp(&self, distance_predicate: T::Scalar) -> Self;
fn simplify_vw(&self, points_to_delete: usize) -> Self;
fn contains_point_inclusive(&self, p: T) -> bool;
fn contains_point_exclusive(&self, p: T) -> bool;
fn convex_hull(&self) -> Result<Vec<T>, LinestringError>;
fn convex_hull_par(&self, per_thread_chunk_size: usize) -> Result<Vec<T>, LinestringError>;
fn apply<F>(&mut self, f: &F)
where
F: Fn(T) -> T;
fn discretize(&self, distance: T::Scalar) -> DiscreteLineSegmentIterator<'_, T>;
}
#[derive(Copy, Clone)]
pub struct Line2<T> {
pub start: T,
pub end: T,
}
impl<T: GenericVector2> Line2<T> {
pub fn new(start: T, end: T) -> Self {
Self { start, end }
}
pub fn intersection_point(self, other: Self) -> Option<Intersection<T>> {
if self.start.is_ulps_eq(
self.end,
T::Scalar::default_epsilon(),
T::Scalar::default_max_ulps(),
) {
return intersect_line_point(other, self.start);
} else if other.start.is_ulps_eq(
other.end,
T::Scalar::default_epsilon(),
T::Scalar::default_max_ulps(),
) {
return intersect_line_point(self, other.start);
}
let self_min_x = self.start.x().min(self.end.x());
let self_max_x = self.start.x().max(self.end.x());
let self_min_y = self.start.y().min(self.end.y());
let self_max_y = self.start.y().max(self.end.y());
let other_min_x = other.start.x().min(other.end.x());
let other_max_x = other.start.x().max(other.end.x());
let other_min_y = other.start.y().min(other.end.y());
let other_max_y = other.start.y().max(other.end.y());
if self_max_x < other_min_x
|| self_min_x > other_max_x
|| self_max_y < other_min_y
|| self_min_y > other_max_y
{
return None;
}
let mut p = self.start;
let mut q = other.start;
let mut r = self.end - p;
let mut s = other.end - q;
let r_cross_s = r.perp_dot(s);
if ulps_eq!(r_cross_s, T::Scalar::ZERO) {
if Self::precedes(self.start, self.end) {
p = self.end;
r = -r;
}
if Self::precedes(other.start, other.end) {
q = other.end;
s = -s;
}
let q_minus_p = q - p;
let q_minus_p_cross_r = q_minus_p.perp_dot(r);
ulps_eq!(q_minus_p_cross_r, T::Scalar::ZERO).then(|| {
let r_dot_r = r.dot(r);
let t0 = q_minus_p.dot(r) / r_dot_r;
let t1 = t0 + s.dot(r) / r_dot_r;
let overlap_start = p + r * t0.max(T::Scalar::ZERO);
let overlap_end = p + r * t1.min(T::Scalar::ONE);
if overlap_start == overlap_end {
Intersection::Intersection(overlap_start)
} else {
Intersection::Overlap(Line2::new(overlap_start, overlap_end))
}
})
} else {
let q_minus_p = q - p;
let t = q_minus_p.perp_dot(s) / r_cross_s;
let u = q_minus_p.perp_dot(r) / r_cross_s;
if (T::Scalar::ZERO..=T::Scalar::ONE).contains(&t)
&& (T::Scalar::ZERO..=T::Scalar::ONE).contains(&u)
{
Some(Intersection::Intersection(p + r * t))
} else {
None
}
}
}
#[inline(always)]
fn precedes(a: T, b: T) -> bool {
if a.x() > b.x() {
true
} else {
a.y() > b.y() && ulps_eq!(a.x(), b.x())
}
}
fn intersection_point3(
start: T,
middle: T,
end: T,
) -> Result<Option<Intersection<T>>, LinestringError> {
let middle_sub_start = T::new_2d(middle.x() - start.x(), middle.y() - start.x());
let middle_sub_end = T::new_2d(middle.x() - end.x(), middle.y() - end.y());
let start_is_vertical = ulps_eq!(middle_sub_start.x(), T::Scalar::ZERO);
let end_is_vertical = ulps_eq!(middle_sub_end.x(), T::Scalar::ZERO);
if start_is_vertical && end_is_vertical {
return if middle_sub_start.y().is_sign_negative()
&& !middle_sub_end.y().is_sign_negative()
{
Ok(None)
} else {
if (middle_sub_start.x() * middle_sub_start.x()
+ middle_sub_start.y() * middle_sub_start.y())
< (middle_sub_end.x() * middle_sub_end.x()
+ middle_sub_end.y() * middle_sub_end.y())
{
Ok(Some(Intersection::Overlap(Line2 { start, end: middle })))
} else {
Ok(Some(Intersection::Overlap(Line2 { start: middle, end })))
}
};
} else if start_is_vertical || end_is_vertical {
return Ok(None);
}
let start_slope = middle_sub_start.x() / middle_sub_start.y();
let end_slope = middle_sub_end.x() / middle_sub_end.y();
if !ulps_eq!(&start_slope, &end_slope) {
return Ok(None);
}
if (middle_sub_start.x() * middle_sub_start.x()
+ middle_sub_start.y() * middle_sub_start.y())
< (middle_sub_end.x() * middle_sub_end.x() + middle_sub_end.y() * middle_sub_end.y())
{
Ok(Some(Intersection::Overlap(Line2 { start, end: middle })))
} else {
Ok(Some(Intersection::Overlap(Line2 { start: middle, end })))
}
}
fn triangle_area_squared_times_4(p1: T, p2: T, p3: T) -> T::Scalar {
let area = p1.x() * p2.y() + p2.x() * p3.y() + p3.x() * p1.y()
- p1.x() * p3.y()
- p2.x() * p1.y()
- p3.x() * p2.y();
area * area
}
pub fn copy_to_3d(&self, plane: Plane) -> linestring_3d::Line3<T::Vector3> {
linestring_3d::Line3::new(
plane.point_to_3d::<T::Vector3>(self.start),
plane.point_to_3d::<T::Vector3>(self.end),
)
}
pub fn aabb(&self) -> <T as GenericVector2>::Aabb {
<T as GenericVector2>::Aabb::from_corners(self.start, self.end)
}
pub fn apply<F>(&mut self, f: &F)
where
F: Fn(T) -> T,
{
self.start = f(self.start);
self.end = f(self.end);
}
pub fn apply_new<F: Fn(T) -> T>(self, f: F) -> Self {
Self {
start: f(self.start),
end: f(self.end),
}
}
}
#[derive(Clone, Debug)]
pub struct VoronoiParabolicArc<T: GenericVector2> {
pub segment: Line2<T>,
pub cell_point: T,
pub start_point: T,
pub end_point: T,
}
impl<T: GenericVector2> VoronoiParabolicArc<T> {
pub fn new(segment: Line2<T>, cell_point: T, start_point: T, end_point: T) -> Self {
Self {
segment,
cell_point,
start_point,
end_point,
}
}
pub fn discretize_2d(&self, max_dist: T::Scalar) -> Vec<T> {
let mut rv = vec![self.start_point];
let segm_vec_x = self.segment.end.x() - self.segment.start.x();
let segm_vec_y = self.segment.end.y() - self.segment.start.y();
let sqr_segment_length = segm_vec_x * segm_vec_x + segm_vec_y * segm_vec_y;
let projection_start =
Self::point_projection(self.start_point, self.segment) * sqr_segment_length;
let projection_end =
Self::point_projection(self.end_point, self.segment) * sqr_segment_length;
let point_vec_x = self.cell_point.x() - self.segment.start.x();
let point_vec_y = self.cell_point.y() - self.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 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 <= max_dist_transformed {
let _ = point_stack.pop();
let inter_x = (segm_vec_x * new_x - segm_vec_y * new_y) / sqr_segment_length
+ self.segment.start.x();
let inter_y = (segm_vec_x * new_y + segm_vec_y * new_x) / sqr_segment_length
+ self.segment.start.y();
let p = T::new_2d(inter_x, inter_y);
rv.push(p);
cur_x = new_x;
cur_y = new_y;
} else {
point_stack.push(mid_x);
}
}
let last_position = rv.len() - 1;
rv[last_position] = self.end_point;
rv
}
pub fn discretize_3d_straight_line(&self) -> Vec<T::Vector3> {
let mut rv = Vec::default();
let distance =
-distance_to_line_squared_safe(self.segment.start, self.segment.end, self.start_point)
.sqrt();
rv.push(T::Vector3::new_3d(
self.start_point.x(),
self.start_point.y(),
distance,
));
let distance = -self.end_point.distance(self.cell_point);
rv.push(T::Vector3::new_3d(
self.end_point.x(),
self.end_point.y(),
distance,
));
rv
}
pub fn discretize_3d(&self, max_dist: T::Scalar) -> Vec<T::Vector3> {
let mut rv = Vec::default();
let z_comp = -self.start_point.distance(self.cell_point);
rv.push(T::Vector3::new_3d(
self.start_point.x(),
self.start_point.y(),
z_comp,
));
let z_comp = -self.end_point.distance(self.cell_point);
rv.push(T::Vector3::new_3d(
self.end_point.x(),
self.end_point.y(),
z_comp,
));
let segm_vec_x = self.segment.end.x() - self.segment.start.x();
let segm_vec_y = self.segment.end.y() - self.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_3d(&rv[0], &self.segment);
let projection_end = sqr_segment_length * Self::point_projection_3d(&rv[1], &self.segment);
let point_vec_x = self.cell_point.x() - self.segment.start.x();
let point_vec_y = self.cell_point.y() - self.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 = (*rv)[1];
let _ = rv.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 <= max_dist_transformed {
let _ = point_stack.pop();
let inter_x = (segm_vec_x * new_x - segm_vec_y * new_y) / sqr_segment_length
+ self.segment.start.x();
let inter_y = (segm_vec_x * new_y + segm_vec_y * new_x) / sqr_segment_length
+ self.segment.start.y();
let z_comp = -T::new_2d(inter_x, inter_y).distance(self.cell_point);
rv.push(T::Vector3::new_3d(inter_x, inter_y, z_comp));
cur_x = new_x;
cur_y = new_y;
} else {
point_stack.push(mid_x);
}
}
let last_position = rv.len() - 1;
rv[last_position] = last_point;
rv
}
#[inline(always)]
#[allow(clippy::suspicious_operation_groupings)]
fn parabola_y(x: T::Scalar, a: T::Scalar, b: T::Scalar) -> T::Scalar {
((x - a) * (x - a) + b * b) / (b + b)
}
#[inline(always)]
fn point_projection(point: T, segment: Line2<T>) -> T::Scalar {
let segment_vec_x = segment.end.x() - segment.start.x();
let segment_vec_y = segment.end.y() - segment.start.y();
let point_vec_x = point.x() - segment.start.x();
let point_vec_y = point.y() - 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
}
fn point_projection_3d(point: &T::Vector3, segment: &Line2<T>) -> T::Scalar {
let segment_vec_x = segment.end.x() - segment.start.x();
let segment_vec_y = segment.end.y() - segment.start.y();
let point_vec_x = point.x() - segment.start.x();
let point_vec_y = point.y() - 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
}
}
struct PriorityDistance<T: GenericVector2> {
key: T::Scalar,
index: usize,
}
#[doc(hidden)]
pub struct WindowIterator<'a, T: GenericVector2>(std::slice::Windows<'a, T>);
impl<T: GenericVector2> WindowIterator<'_, T> {
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.0.len() == 0
}
pub fn len(&self) -> usize {
self.0.len()
}
}
#[doc(hidden)]
pub struct ChunkIterator<'a, T: GenericVector2>(std::slice::ChunksExact<'a, T>);
impl<'a, T: GenericVector2> ChunkIterator<'a, T> {
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.0.len() == 0
}
#[inline(always)]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline(always)]
#[must_use]
pub fn remainder(&self) -> &'a [T] {
self.0.remainder()
}
}
impl<T: GenericVector2> LineString2<T> for Vec<T> {
fn is_connected(&self) -> bool {
self.is_empty() || self.first().unwrap() == self.last().unwrap()
}
fn point_count(&self) -> usize {
if self.len() > 2 && self.is_connected() {
self.len() - 1
} else {
self.len()
}
}
fn is_self_intersecting(&self) -> Result<bool, LinestringError> {
if self.len() <= 2 {
Ok(false)
} else if self.len() < 10 {
let iter = self.window_iter();
let iter_len = iter.len();
for l0 in iter.enumerate().take(iter_len - 1) {
for l1 in self.window_iter().enumerate().skip(l0.0 + 1) {
if l0.0 == l1.0 {
continue;
} else if l0.0 + 1 == l1.0 {
if Line2::intersection_point3(l0.1.start, l0.1.end, l1.1.end)?.is_some() {
return Ok(true);
}
} else if let Some(point) = l0.1.intersection_point(l1.1) {
let point = point.single();
if (point.is_ulps_eq(
l0.1.start,
T::Scalar::default_epsilon(),
T::Scalar::default_max_ulps(),
) || point.is_ulps_eq(
l0.1.end,
T::Scalar::default_epsilon(),
T::Scalar::default_max_ulps(),
)) && (point.is_ulps_eq(
l1.1.start,
T::Scalar::default_epsilon(),
T::Scalar::default_max_ulps(),
) || point.is_ulps_eq(
l1.1.end,
T::Scalar::default_epsilon(),
T::Scalar::default_max_ulps(),
)) {
continue;
} else {
return Ok(true);
}
}
}
}
Ok(false)
} else {
let result = intersection::IntersectionData::default()
.with_ignore_end_point_intersections(true)?
.with_stop_at_first_intersection(true)?
.with_lines(self.window_iter())?
.compute()?;
Ok(result.len() > 0)
}
}
fn intersection(&self, other: &Line2<T>) -> Option<Intersection<T>> {
for l in self.window_iter() {
let intersection = l.intersection_point(*other);
if intersection.is_some() {
return intersection;
}
}
None
}
fn find_two_intersections(&self, other: &Line2<T>) -> (Option<T>, Option<T>) {
let mut intersections: Vec<T> = Vec::new();
for edge in self.window_iter() {
let intersection = edge.intersection_point(*other);
match intersection {
Some(Intersection::Intersection(int_pt)) => {
if !intersections.contains(&int_pt) {
intersections.push(int_pt);
}
}
Some(Intersection::Overlap(overlap_line)) => {
if !intersections.contains(&overlap_line.start) {
intersections.push(overlap_line.start);
}
if intersections.len() < 2 && !intersections.contains(&overlap_line.end) {
intersections.push(overlap_line.end);
}
}
None => {}
}
if intersections.len() == 2 {
break;
}
}
let first = intersections.first().cloned();
let second = intersections.get(1).cloned();
(first, second)
}
fn find_all_intersections(&self, other: &Line2<T>) -> Vec<Intersection<T>> {
let mut intersections: Vec<Intersection<T>> = Vec::new();
for edge in self.window_iter() {
if let Some(intersection) = edge.intersection_point(*other) {
intersections.push(intersection);
}
}
intersections
}
fn ray_edge_intersection(origin: T, ray_dir: T, p0: T, p1: T) -> Option<T::Scalar> {
let s = p1.sub(p0);
let r_cross_s = ray_dir.perp_dot(s);
if ulps_eq!(r_cross_s, T::Scalar::zero()) {
let q_minus_p = p0.sub(origin);
let q_minus_p_cross_r = q_minus_p.perp_dot(ray_dir);
ulps_eq!(q_minus_p_cross_r, T::Scalar::zero()).then(|| {
let r_dot_r = ray_dir.dot(ray_dir);
let t0 = q_minus_p.dot(ray_dir) / r_dot_r;
let t1: T::Scalar = t0 + s.dot(ray_dir) / r_dot_r;
t0.min(t1)
})
} else {
let q_minus_p = p0.sub(origin);
let t = q_minus_p.perp_dot(s) / r_cross_s;
let u = q_minus_p.perp_dot(ray_dir) / r_cross_s;
(t >= T::Scalar::ZERO && (T::Scalar::ZERO..=T::Scalar::one()).contains(&u)).then_some(t)
}
}
fn closest_ray_intersection(&self, ray_dir: T, origin: T) -> Option<T> {
let mut nearest_t: Option<T::Scalar> = None;
if self.len() <= 1 {
return None;
}
self.windows(2).for_each(|v| {
match (
Self::ray_edge_intersection(origin, ray_dir, v[0], v[1]),
nearest_t,
) {
(Some(t), Some(nt)) if t > T::Scalar::ZERO && t < nt => nearest_t = Some(t),
(Some(t), None) if t > T::Scalar::ZERO => nearest_t = Some(t),
_ => (),
}
});
nearest_t.map(|t| origin + ray_dir * t)
}
fn window_iter(&self) -> WindowIterator<'_, T> {
WindowIterator(self.windows(2))
}
fn chunk_iter(&self) -> ChunkIterator<'_, T> {
ChunkIterator(self.chunks_exact(2))
}
fn copy_to_3d(&self, plane: Plane) -> Vec<T::Vector3> {
plane.points_to_3d(self.iter()).collect()
}
fn convex_hull(&self) -> Result<Vec<T>, LinestringError> {
convex_hull::graham_scan(self)
}
fn convex_hull_par(&self, per_thread_chunk_size: usize) -> Result<Vec<T>, LinestringError> {
assert!(per_thread_chunk_size > 1);
let indices: Vec<u32> = (0..self.len() as u32).collect();
Ok(
convex_hull::convex_hull_par(self, &indices, per_thread_chunk_size)?
.iter()
.map(|i| self[*i as usize])
.collect(),
)
}
fn simplify_rdp(&self, distance_predicate: T::Scalar) -> Self {
if self.len() <= 2 {
return self.clone();
}
let mut rv: Vec<T> = Vec::with_capacity(self.len());
rv.push(self[0]);
simplify::simplify_rdp_recursive(&mut rv, self, distance_predicate * distance_predicate);
rv
}
#[inline(always)]
fn simplify_vw(&self, points_to_delete: usize) -> Self {
simplify::simplify_vw(self, points_to_delete)
}
#[inline(always)]
fn contains_point_inclusive(&self, p: T) -> bool {
self.is_connected() && convex_hull::contains_point_inclusive(self, p)
}
#[inline(always)]
fn contains_point_exclusive(&self, p: T) -> bool {
self.is_connected() && convex_hull::contains_point_exclusive(self, p)
}
fn apply<F>(&mut self, f: &F)
where
F: Fn(T) -> T,
{
for v in self.iter_mut() {
*v = f(*v)
}
}
fn discretize(&self, distance: T::Scalar) -> DiscreteLineSegmentIterator<'_, T> {
DiscreteLineSegmentIterator::new(self, distance)
}
}
pub trait Approx<T: GenericVector2> {
fn ulps_eq(
&self,
other: &Self,
epsilon: <T::Scalar as AbsDiffEq>::Epsilon,
max_ulps: u32,
) -> bool;
fn abs_diff_eq(&self, other: &Self, epsilon: <T::Scalar as AbsDiffEq>::Epsilon) -> bool;
}
impl<T: GenericVector2> Approx<T> for Vec<T> {
fn ulps_eq(
&self,
other: &Self,
epsilon: <T::Scalar as AbsDiffEq>::Epsilon,
max_ulps: u32,
) -> bool {
self.len() == other.len()
&& self
.iter()
.zip(other.iter())
.all(|(a, b)| a.is_ulps_eq(*b, epsilon, max_ulps))
}
fn abs_diff_eq(&self, other: &Self, epsilon: <T::Scalar as AbsDiffEq>::Epsilon) -> bool {
self.len() == other.len()
&& self
.iter()
.zip(other.iter())
.all(|(a, b)| a.is_abs_diff_eq(*b, epsilon))
}
}
pub fn intersect_line_point<T: GenericVector2>(
line: Line2<T>,
point: T,
) -> Option<Intersection<T>> {
if ulps_eq!(line.start.x(), point.x()) && ulps_eq!(line.start.y(), point.y()) {
return Some(Intersection::Intersection(point));
}
if ulps_eq!(line.end.x(), point.x()) && ulps_eq!(line.end.y(), point.y()) {
return Some(Intersection::Intersection(point));
}
let l0x = line.start.x();
let l1x = line.end.x();
let l0y = line.start.y();
let l1y = line.end.y();
let px = point.x();
let py = point.y();
let ab = ((l1x - l0x) * (l1x - l0x) + (l1y - l0y) * (l1y - l0y)).sqrt();
let ap = ((px - l0x) * (px - l0x) + (py - l0y) * (py - l0y)).sqrt();
let pb = ((l1x - px) * (l1x - px) + (l1y - py) * (l1y - py)).sqrt();
if ulps_eq!(&ab, &(ap + pb)) {
return Some(Intersection::Intersection(point));
}
None
}
pub enum Intersection<T> {
Intersection(T),
Overlap(Line2<T>),
}
impl<T: GenericVector2> Intersection<T> {
pub fn single(&self) -> T {
match self {
Self::Overlap(a) => a.start,
Self::Intersection(a) => *a,
}
}
pub fn closest(&self, other: T) -> T {
match self {
Self::Overlap(a) => {
if other.distance_sq(a.start) < other.distance_sq(a.end) {
a.start
} else {
a.end
}
}
Self::Intersection(a) => *a,
}
}
}
#[inline(always)]
pub fn scale_to_coordinate<T: GenericVector2>(point: T, vector: T, scale: T::Scalar) -> T {
point + vector * scale
}
#[inline(always)]
pub fn distance_to_line_squared<T: GenericVector2>(l0: T, l1: T, p: T) -> T::Scalar {
let l0_sub_l1 = l0 - l1;
let l0_sub_l1_sq_mag = l0_sub_l1.magnitude_sq();
let l0_sub_p = l0 - p;
let dot = l0_sub_p.dot(l0_sub_l1) / l0_sub_l1_sq_mag;
if dot < T::Scalar::ZERO {
l0_sub_p.magnitude_sq()
} else if dot > T::Scalar::ONE {
(l1 - p).magnitude_sq()
} else {
let a_sub_p_cross_a_sub_b = l0_sub_p.perp_dot(l0_sub_l1);
a_sub_p_cross_a_sub_b * a_sub_p_cross_a_sub_b / l0_sub_l1_sq_mag
}
}
#[inline(always)]
pub fn distance_to_line_squared_safe<T: GenericVector2>(l0: T, l1: T, p: T) -> T::Scalar {
if l0.is_ulps_eq(
l1,
T::Scalar::default_epsilon(),
T::Scalar::default_max_ulps(),
) {
l0.distance_sq(p)
} else {
distance_to_line_squared(l0, l1, p)
}
}
#[derive(PartialEq, Clone, Debug)]
pub struct SimpleAffine<T: GenericVector2>
where
T::Aabb: Aabb2<Vector = T>,
{
pub a_offset: [T::Scalar; 2],
pub scale: [T::Scalar; 2],
pub b_offset: [T::Scalar; 2],
}
impl<T: GenericVector2> SimpleAffine<T>
where
T::Aabb: Aabb2<Vector = T>,
{
pub fn new<A>(a_aabb: &<T as GenericVector2>::Aabb, b_aabb: &A) -> Result<Self, LinestringError>
where
A: Aabb2<Vector = T>,
{
let min_dim = T::Scalar::ONE;
let two = T::Scalar::TWO;
if a_aabb.is_empty() || b_aabb.is_empty() {
return Err(LinestringError::AabbError(
"one or both AABBs are empty".to_string(),
));
}
let (source_low, source_high, source_delta) = a_aabb.extents();
let (destination_low, destination_high, destination_delta) = b_aabb.extents();
let source_aabb_center = [
-(source_low.x() + source_high.x()) / two,
-(source_low.y() + source_high.y()) / two,
];
let source_aabb_size = [source_delta.x().max(min_dim), source_delta.y().max(min_dim)];
let dest_aabb_center = [
(destination_low.x() + destination_high.x()) / two,
(destination_low.y() + destination_high.y()) / two,
];
let dest_aabb_size = [
destination_delta.x().max(min_dim),
destination_delta.y().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;
Ok(Self {
a_offset: source_aabb_center,
scale: [scale, scale],
b_offset: dest_aabb_center,
})
}
#[inline(always)]
pub fn transform_ba(&self, point: T) -> Result<T, LinestringError> {
let x = (point.x() - self.b_offset[0]) / self.scale[0] - self.a_offset[0];
let y = (point.y() - self.b_offset[1]) / self.scale[1] - self.a_offset[1];
if x.is_finite() && y.is_finite() {
Ok(T::new_2d(x, y))
} else {
Err(LinestringError::TransformError(
"Transformation out of bounds".to_string(),
))
}
}
#[inline(always)]
pub fn transform_ab(&self, point: T) -> Result<T, LinestringError> {
let x = (point.x() + self.a_offset[0]) * self.scale[0] + self.b_offset[0];
let y = (point.y() + self.a_offset[1]) * self.scale[1] + self.b_offset[1];
if x.is_finite() && y.is_finite() {
Ok(T::new_2d(x, y))
} else {
Err(LinestringError::TransformError(
"Transformation out of bounds".to_string(),
))
}
}
#[inline(always)]
pub fn transform_ba_a(
&self,
points: [T::Scalar; 4],
) -> Result<[T::Scalar; 4], LinestringError> {
let x1 = (points[0] - self.b_offset[0]) / self.scale[0] - self.a_offset[0];
let y1 = (points[1] - self.b_offset[1]) / self.scale[1] - self.a_offset[1];
let x2 = (points[2] - self.b_offset[0]) / self.scale[0] - self.a_offset[0];
let y2 = (points[3] - self.b_offset[1]) / self.scale[1] - self.a_offset[1];
if x1.is_finite() && y1.is_finite() && x2.is_finite() && y2.is_finite() {
Ok([x1, y1, x2, y2])
} else {
Err(LinestringError::TransformError(
"Transformation out of bounds".to_string(),
))
}
}
#[inline(always)]
pub fn transform_ab_a(
&self,
points: [T::Scalar; 4],
) -> Result<[T::Scalar; 4], LinestringError> {
let x1 = (points[0] + self.a_offset[0]) * self.scale[0] + self.b_offset[0];
let y1 = (points[1] + self.a_offset[1]) * self.scale[1] + self.b_offset[1];
let x2 = (points[2] + self.a_offset[0]) * self.scale[0] + self.b_offset[0];
let y2 = (points[3] + self.a_offset[1]) * self.scale[1] + self.b_offset[1];
if x1.is_finite() && y1.is_finite() && x2.is_finite() && y2.is_finite() {
Ok([x1, y1, x2, y2])
} else {
Err(LinestringError::TransformError(
"Transformation out of bounds".to_string(),
))
}
}
}
pub struct DiscreteLineSegmentIterator<'a, T: GenericVector2> {
points: &'a Vec<T>,
distance: T::Scalar,
current_index: usize,
substep_index: usize,
num_substeps: usize,
step: T,
}
impl<'a, T: GenericVector2> DiscreteLineSegmentIterator<'a, T> {
fn new(points: &'a Vec<T>, distance: T::Scalar) -> Self {
DiscreteLineSegmentIterator {
points,
distance,
current_index: 0,
substep_index: 0,
num_substeps: 0,
step: T::new_2d(T::Scalar::ZERO, T::Scalar::ZERO),
}
}
fn initialize_step(&mut self) {
let start_point = self.points[self.current_index];
let end_point = self.points[self.current_index + 1];
let direction = end_point - start_point;
let segment_length = direction.magnitude();
if segment_length > T::Scalar::ZERO {
let num_substeps = (segment_length / self.distance).ceil();
self.num_substeps = num_substeps.as_();
self.step = direction / num_substeps;
} else {
self.num_substeps = 1;
self.step = T::new_2d(T::Scalar::ZERO, T::Scalar::ZERO);
}
}
}
impl<T: GenericVector2> Iterator for DiscreteLineSegmentIterator<'_, T>
where
usize: AsPrimitive<<T as HasXY>::Scalar>,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.points.is_empty() {
return None;
}
match self.points.len() - 1 {
n if self.current_index < n => {
if self.substep_index == 0 {
self.initialize_step();
}
if self.substep_index < self.num_substeps {
let start_point = self.points[self.current_index];
let result = start_point + self.step * (self.substep_index.as_());
self.substep_index += 1;
return Some(result);
}
self.current_index += 1;
self.substep_index = 0;
self.next()
}
n if self.current_index == n => {
self.current_index += 1;
self.substep_index = 0;
self.points.last().copied()
}
_ => None,
}
}
}