use crate::{
line::Line,
segment::{ArcSegment, LineSegment, Segment},
};
use approx::UlpsEq;
#[derive(Clone, Copy, Debug)]
pub enum PrimitiveIntersections {
Zero,
One([f64; 2]),
Two([[f64; 2]; 2]),
}
impl PartialEq for PrimitiveIntersections {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::One(l), Self::One(r)) => l == r,
(Self::Two(l), Self::Two(r)) => {
(l[0] == r[0] && l[1] == r[1]) || (l[0] == r[1] && l[1] == r[0])
}
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
}
}
}
impl IntoIterator for PrimitiveIntersections {
type Item = [f64; 2];
type IntoIter = PrimitiveIntersectionsIntoIter;
fn into_iter(self) -> Self::IntoIter {
let intersections = match self {
PrimitiveIntersections::Zero => [None, None],
PrimitiveIntersections::One(i) => [Some(i), None],
PrimitiveIntersections::Two(ai) => [Some(ai[0]), Some(ai[1])],
};
return PrimitiveIntersectionsIntoIter {
intersections,
counter: 0,
};
}
}
impl approx::AbsDiffEq for PrimitiveIntersections {
type Epsilon = f64;
fn default_epsilon() -> f64 {
f64::default_epsilon()
}
fn abs_diff_eq(&self, other: &Self, epsilon: f64) -> bool {
match (self, other) {
(Self::One(l), Self::One(r)) => l.abs_diff_eq(r, epsilon),
(Self::Two(l), Self::Two(r)) => {
(l[0].abs_diff_eq(&r[0], epsilon) && l[1].abs_diff_eq(&r[1], epsilon))
|| (l[0].abs_diff_eq(&r[1], epsilon) && l[1].abs_diff_eq(&r[0], epsilon))
}
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
}
}
}
impl approx::RelativeEq for PrimitiveIntersections {
fn default_max_relative() -> f64 {
f64::default_max_relative()
}
fn relative_eq(&self, other: &Self, epsilon: f64, max_relative: f64) -> bool {
match (self, other) {
(Self::One(l), Self::One(r)) => l.relative_eq(r, epsilon, max_relative),
(Self::Two(l), Self::Two(r)) => {
(l[0].relative_eq(&r[0], epsilon, max_relative)
&& l[1].relative_eq(&r[1], epsilon, max_relative))
|| (l[0].relative_eq(&r[1], epsilon, max_relative)
&& l[1].relative_eq(&r[0], epsilon, max_relative))
}
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
}
}
}
impl approx::UlpsEq for PrimitiveIntersections {
fn default_max_ulps() -> u32 {
f64::default_max_ulps()
}
fn ulps_eq(&self, other: &Self, epsilon: f64, max_ulps: u32) -> bool {
match (self, other) {
(Self::One(l), Self::One(r)) => l.ulps_eq(r, epsilon, max_ulps),
(Self::Two(l), Self::Two(r)) => {
(l[0].ulps_eq(&r[0], epsilon, max_ulps) && l[1].ulps_eq(&r[1], epsilon, max_ulps))
|| (l[0].ulps_eq(&r[1], epsilon, max_ulps)
&& l[1].ulps_eq(&r[0], epsilon, max_ulps))
}
_ => core::mem::discriminant(self) == core::mem::discriminant(other),
}
}
}
pub struct PrimitiveIntersectionsIntoIter {
intersections: [Option<[f64; 2]>; 2],
counter: usize,
}
impl Iterator for PrimitiveIntersectionsIntoIter {
type Item = [f64; 2];
fn next(&mut self) -> Option<Self::Item> {
let i = self.intersections.get_mut(self.counter)?.take()?;
self.counter += 1;
return Some(i);
}
}
impl std::ops::Index<usize> for PrimitiveIntersections {
type Output = [f64; 2];
fn index(&self, index: usize) -> &Self::Output {
self.get(index).expect("index is out of bounds")
}
}
impl PrimitiveIntersections {
pub fn iter(&self) -> PrimitiveIntersectionsIter<'_> {
return PrimitiveIntersectionsIter {
intersections: self,
counter: 0,
};
}
pub fn get(&self, index: usize) -> Option<&[f64; 2]> {
match self {
PrimitiveIntersections::Zero => None,
PrimitiveIntersections::One(i) => {
if index == 0 {
return Some(i);
} else {
return None;
}
}
PrimitiveIntersections::Two(ai) => ai.get(index),
}
}
pub fn get_mut(&mut self, index: usize) -> Option<&mut [f64; 2]> {
match self {
PrimitiveIntersections::Zero => None,
PrimitiveIntersections::One(i) => {
if index == 0 {
return Some(i);
} else {
return None;
}
}
PrimitiveIntersections::Two(ai) => ai.get_mut(index),
}
}
pub fn len(&self) -> usize {
match self {
PrimitiveIntersections::Zero => 0,
PrimitiveIntersections::One(_) => 1,
PrimitiveIntersections::Two(_) => 2,
}
}
pub(crate) fn push(&mut self, intersection: [f64; 2]) -> bool {
match self {
PrimitiveIntersections::Zero => {
*self = PrimitiveIntersections::One(intersection);
return true;
}
PrimitiveIntersections::One(i) => {
*self = PrimitiveIntersections::Two([*i, intersection]);
return true;
}
PrimitiveIntersections::Two(_) => {
return false;
}
}
}
}
pub struct PrimitiveIntersectionsIter<'a> {
intersections: &'a PrimitiveIntersections,
counter: usize,
}
impl<'a> Iterator for PrimitiveIntersectionsIter<'a> {
type Item = &'a [f64; 2];
fn next(&mut self) -> Option<Self::Item> {
match self.intersections {
PrimitiveIntersections::Zero => return None,
PrimitiveIntersections::One(i) => {
if self.counter == 0 {
self.counter += 1;
Some(i)
} else {
return None;
}
}
PrimitiveIntersections::Two(ai) => {
let i = match self.counter {
0 => &ai[0],
1 => &ai[1],
_ => return None,
};
self.counter += 1;
return Some(i);
}
}
}
}
pub(crate) mod private {
pub trait Sealed {}
}
pub trait Primitive: private::Sealed {
fn contains_point(&self, point: [f64; 2], epsilon: f64, max_ulps: u32) -> bool;
fn intersections_point(
&self,
point: [f64; 2],
epsilon: f64,
max_ulps: u32,
) -> PrimitiveIntersections {
if self.contains_point(point, epsilon, max_ulps) {
return PrimitiveIntersections::One(point);
}
return PrimitiveIntersections::Zero;
}
fn intersections_line(
&self,
line: &Line,
epsilon: f64,
max_ulps: u32,
) -> PrimitiveIntersections;
fn intersections_line_segment(
&self,
line_segment: &LineSegment,
epsilon: f64,
max_ulps: u32,
) -> PrimitiveIntersections;
fn intersections_arc_segment(
&self,
arc_segment: &ArcSegment,
epsilon: f64,
max_ulps: u32,
) -> PrimitiveIntersections;
fn intersections_segment(
&self,
segment: &Segment,
epsilon: f64,
max_ulps: u32,
) -> PrimitiveIntersections {
match segment {
Segment::LineSegment(line_segment) => {
self.intersections_line_segment(line_segment, epsilon, max_ulps)
}
Segment::ArcSegment(arc_segment) => {
self.intersections_arc_segment(arc_segment, epsilon, max_ulps)
}
}
}
fn intersections_primitive<T: Primitive>(
&self,
other: &T,
epsilon: f64,
max_ulps: u32,
) -> PrimitiveIntersections
where
Self: Sized;
fn intersections<'a, T: Into<crate::geometry::GeometryRef<'a>>>(
&self,
other: T,
epsilon: f64,
max_ulps: u32,
) -> Vec<crate::composite::Intersection>
where
Self: Sized,
{
let geo_ref: crate::geometry::GeometryRef = other.into();
return geo_ref.intersections_primitive(self, epsilon, max_ulps);
}
}
impl private::Sealed for [f64; 2] {}
impl Primitive for [f64; 2] {
fn contains_point(&self, point: [f64; 2], epsilon: f64, max_ulps: u32) -> bool {
return self.ulps_eq(&point, epsilon, max_ulps);
}
fn intersections_line(
&self,
line: &Line,
epsilon: f64,
max_ulps: u32,
) -> PrimitiveIntersections {
line.intersections_point(*self, epsilon, max_ulps)
}
fn intersections_line_segment(
&self,
line_segment: &LineSegment,
epsilon: f64,
max_ulps: u32,
) -> PrimitiveIntersections {
line_segment.intersections_point(*self, epsilon, max_ulps)
}
fn intersections_arc_segment(
&self,
arc_segment: &ArcSegment,
epsilon: f64,
max_ulps: u32,
) -> PrimitiveIntersections {
arc_segment.intersections_point(*self, epsilon, max_ulps)
}
fn intersections_primitive<T: Primitive>(
&self,
other: &T,
epsilon: f64,
max_ulps: u32,
) -> PrimitiveIntersections {
other.intersections_point(*self, epsilon, max_ulps)
}
}