use std::{
cmp::{self, Ordering},
hash::{Hash, Hasher},
};
use fj_math::Point;
use crate::{objects::Vertex, storage::HandleWrapper};
#[derive(Clone, Copy, Debug)]
pub struct CurveBoundary<T: CurveBoundaryElement> {
pub inner: [T::Repr; 2],
}
impl<T: CurveBoundaryElement> CurveBoundary<T> {
pub fn is_normalized(&self) -> bool {
let [a, b] = &self.inner;
a <= b
}
#[must_use]
pub fn reverse(self) -> Self {
let [a, b] = self.inner;
Self { inner: [b, a] }
}
#[must_use]
pub fn normalize(self) -> Self {
if self.is_normalized() {
self
} else {
self.reverse()
}
}
}
impl CurveBoundary<Point<1>> {
pub fn is_empty(&self) -> bool {
let [min, max] = &self.inner;
min >= max
}
pub fn contains(&self, point: Point<1>) -> bool {
let [min, max] = self.normalize().inner;
point > min && point < max
}
pub fn overlaps(&self, other: &Self) -> bool {
let [a_low, a_high] = self.normalize().inner;
let [b_low, b_high] = other.normalize().inner;
a_low <= b_high && a_high >= b_low
}
pub fn difference(self, other: Self) -> Option<OneOrTwoBoundaries> {
let [self_min, self_max] = self.normalize().inner;
let [other_min, other_max] = other.normalize().inner;
match (
self_min <= other_min,
self_min <= other_max,
self_max <= other_min,
self_max <= other_max,
) {
(true, true, true, true) => {
Some(OneOrTwoBoundaries::One(CurveBoundary {
inner: [self_min, self_max],
}))
}
(true, true, false, true) => {
let min = self_min;
let max = other_min;
if min == max {
return None;
}
Some(OneOrTwoBoundaries::One(CurveBoundary {
inner: [min, max],
}))
}
(true, true, false, false) => Some(OneOrTwoBoundaries::Two([
CurveBoundary {
inner: [self_min, other_min],
},
CurveBoundary {
inner: [other_max, self_max],
},
])),
(false, true, false, true) => None,
(false, true, false, false) => {
Some(OneOrTwoBoundaries::One(CurveBoundary {
inner: [other_max, self_max],
}))
}
(false, false, false, false) => {
Some(OneOrTwoBoundaries::One(CurveBoundary {
inner: [self_min, self_max],
}))
}
case => {
unreachable!(
"{case:?} is impossible, due to normalization above"
);
}
}
}
#[must_use]
pub fn intersection(self, other: Self) -> Self {
let self_ = self.normalize();
let other = other.normalize();
let [self_min, self_max] = self_.inner;
let [other_min, other_max] = other.inner;
let min = cmp::max(self_min, other_min);
let max = cmp::min(self_max, other_max);
Self { inner: [min, max] }
}
pub fn union(self, other: Self) -> Self {
let self_ = self.normalize();
let other = other.normalize();
assert!(
self.overlaps(&other),
"Can't merge boundaries that don't at least touch"
);
let [self_min, self_max] = self_.inner;
let [other_min, other_max] = other.inner;
let min = cmp::min(self_min, other_min);
let max = cmp::max(self_max, other_max);
Self { inner: [min, max] }
}
}
impl<S, T: CurveBoundaryElement> From<[S; 2]> for CurveBoundary<T>
where
S: Into<T::Repr>,
{
fn from(boundary: [S; 2]) -> Self {
let inner = boundary.map(Into::into);
Self { inner }
}
}
impl<T: CurveBoundaryElement> Eq for CurveBoundary<T> {}
impl<T: CurveBoundaryElement> PartialEq for CurveBoundary<T> {
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}
impl<T: CurveBoundaryElement> Hash for CurveBoundary<T> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.inner.hash(state);
}
}
impl<T: CurveBoundaryElement> Ord for CurveBoundary<T> {
fn cmp(&self, other: &Self) -> Ordering {
self.inner.cmp(&other.inner)
}
}
impl<T: CurveBoundaryElement> PartialOrd for CurveBoundary<T> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[derive(Debug, Eq, PartialEq)]
pub enum OneOrTwoBoundaries {
One(CurveBoundary<Point<1>>),
Two([CurveBoundary<Point<1>>; 2]),
}
pub trait CurveBoundaryElement {
type Repr: Eq + Hash + Ord;
}
impl CurveBoundaryElement for Point<1> {
type Repr = Self;
}
impl CurveBoundaryElement for Vertex {
type Repr = HandleWrapper<Vertex>;
}
#[cfg(test)]
mod tests {
use fj_math::Point;
use crate::geometry::{boundary::OneOrTwoBoundaries, CurveBoundary};
#[test]
fn overlaps() {
assert!(overlap([0., 2.], [1., 3.])); assert!(overlap([0., 1.], [1., 2.])); assert!(overlap([2., 0.], [3., 1.])); assert!(overlap([1., 3.], [0., 2.]));
assert!(!overlap([0., 1.], [2., 3.])); assert!(!overlap([2., 3.], [0., 1.]));
fn overlap(a: [f64; 2], b: [f64; 2]) -> bool {
let a = array_to_boundary(a);
let b = array_to_boundary(b);
a.overlaps(&b)
}
}
#[test]
fn difference() {
diff([1., 2.], [1., 2.], &[]);
diff([1., 2.], [2., 1.], &[]);
diff([2., 1.], [1., 2.], &[]);
diff([2., 1.], [2., 1.], &[]);
diff([1., 2.], [1., 3.], &[]);
diff([1., 2.], [3., 1.], &[]);
diff([2., 1.], [1., 3.], &[]);
diff([2., 1.], [3., 1.], &[]);
diff([1., 2.], [0., 2.], &[]);
diff([1., 2.], [2., 0.], &[]);
diff([2., 1.], [0., 2.], &[]);
diff([2., 1.], [2., 0.], &[]);
diff([1., 2.], [0., 3.], &[]);
diff([1., 2.], [3., 0.], &[]);
diff([2., 1.], [0., 3.], &[]);
diff([2., 1.], [3., 0.], &[]);
diff([0., 1.], [1., 2.], &[[0., 1.]]);
diff([0., 1.], [2., 1.], &[[0., 1.]]);
diff([1., 0.], [1., 2.], &[[0., 1.]]);
diff([1., 0.], [2., 1.], &[[0., 1.]]);
diff([0., 1.], [2., 3.], &[[0., 1.]]);
diff([0., 1.], [3., 2.], &[[0., 1.]]);
diff([1., 0.], [2., 3.], &[[0., 1.]]);
diff([1., 0.], [3., 2.], &[[0., 1.]]);
diff([2., 3.], [1., 2.], &[[2., 3.]]);
diff([2., 3.], [2., 1.], &[[2., 3.]]);
diff([3., 2.], [1., 2.], &[[2., 3.]]);
diff([3., 2.], [2., 1.], &[[2., 3.]]);
diff([2., 3.], [0., 1.], &[[2., 3.]]);
diff([2., 3.], [1., 0.], &[[2., 3.]]);
diff([3., 2.], [0., 1.], &[[2., 3.]]);
diff([3., 2.], [1., 0.], &[[2., 3.]]);
diff([0., 2.], [1., 3.], &[[0., 1.]]);
diff([0., 2.], [3., 1.], &[[0., 1.]]);
diff([2., 0.], [1., 3.], &[[0., 1.]]);
diff([2., 0.], [3., 1.], &[[0., 1.]]);
diff([1., 3.], [0., 2.], &[[2., 3.]]);
diff([1., 3.], [2., 0.], &[[2., 3.]]);
diff([3., 1.], [0., 2.], &[[2., 3.]]);
diff([3., 1.], [2., 0.], &[[2., 3.]]);
diff([0., 3.], [1., 2.], &[[0., 1.], [2., 3.]]);
diff([0., 3.], [2., 1.], &[[0., 1.], [2., 3.]]);
diff([3., 0.], [1., 2.], &[[0., 1.], [2., 3.]]);
diff([3., 0.], [2., 1.], &[[0., 1.], [2., 3.]]);
fn diff(a: [f64; 2], b: [f64; 2], x: &[[f64; 2]]) {
print!("{a:?} \\ {b:?} = ");
let a = array_to_boundary(a);
let b = array_to_boundary(b);
let x = match x {
[] => None,
&[x] => Some(OneOrTwoBoundaries::One(array_to_boundary(x))),
&[x1, x2] => Some(OneOrTwoBoundaries::Two(
[x1, x2].map(array_to_boundary),
)),
_ => panic!("Invalid result"),
};
let diff = a.difference(b);
println!("{diff:?}");
assert_eq!(diff, x);
}
}
fn array_to_boundary(array: [f64; 2]) -> CurveBoundary<Point<1>> {
CurveBoundary::from(array.map(|element| [element]))
}
}