blob: 806de65d90616461a23532d4fde6556281903d17 [file] [log] [blame]
use {Rect, Box2D, Box3D, Vector2D, Vector3D, size2, point2, point3};
use approxord::{min, max};
use num::Zero;
use core::ops::Deref;
use core::ops::{Add, Sub};
use core::cmp::{PartialEq};
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "serde", serde(transparent))]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub struct NonEmpty<T>(pub(crate) T);
impl<T> Deref for NonEmpty<T> {
type Target = T;
fn deref(&self) -> &T {
&self.0
}
}
impl<T> NonEmpty<T> {
#[inline]
pub fn get(&self) -> &T {
&self.0
}
}
impl<T, U> NonEmpty<Rect<T, U>>
where
T: Copy + Clone + Zero + PartialOrd + PartialEq + Add<T, Output = T> + Sub<T, Output = T>,
{
#[inline]
pub fn union(&self, other: &NonEmpty<Rect<T, U>>) -> NonEmpty<Rect<T, U>> {
let origin = point2(
min(self.min_x(), other.min_x()),
min(self.min_y(), other.min_y()),
);
let lower_right_x = max(self.max_x(), other.max_x());
let lower_right_y = max(self.max_y(), other.max_y());
NonEmpty(Rect {
origin,
size: size2(
lower_right_x - origin.x,
lower_right_y - origin.y,
),
})
}
#[inline]
pub fn contains_rect(&self, rect: &Self) -> bool {
self.min_x() <= rect.min_x()
&& rect.max_x() <= self.max_x()
&& self.min_y() <= rect.min_y()
&& rect.max_y() <= self.max_y()
}
#[inline]
pub fn translate(&self, by: Vector2D<T, U>) -> Self {
NonEmpty(self.0.translate(by))
}
}
impl<T, U> NonEmpty<Box2D<T, U>>
where
T: Copy + PartialOrd,
{
#[inline]
pub fn union(&self, other: &NonEmpty<Box2D<T, U>>) -> NonEmpty<Box2D<T, U>> {
NonEmpty(Box2D {
min: point2(
min(self.min.x, other.min.x),
min(self.min.y, other.min.y),
),
max: point2(
max(self.max.x, other.max.x),
max(self.max.y, other.max.y),
),
})
}
/// Returns true if this box contains the interior of the other box.
#[inline]
pub fn contains_box(&self, other: &Self) -> bool {
self.min.x <= other.min.x
&& other.max.x <= self.max.x
&& self.min.y <= other.min.y
&& other.max.y <= self.max.y
}
}
impl<T, U> NonEmpty<Box2D<T, U>>
where
T: Copy + Add<T, Output = T>,
{
#[inline]
pub fn translate(&self, by: Vector2D<T, U>) -> Self {
NonEmpty(self.0.translate(by))
}
}
impl<T, U> NonEmpty<Box3D<T, U>>
where
T: Copy + PartialOrd,
{
#[inline]
pub fn union(&self, other: &NonEmpty<Box3D<T, U>>) -> NonEmpty<Box3D<T, U>> {
NonEmpty(Box3D {
min: point3(
min(self.min.x, other.min.x),
min(self.min.y, other.min.y),
min(self.min.z, other.min.z),
),
max: point3(
max(self.max.x, other.max.x),
max(self.max.y, other.max.y),
max(self.max.z, other.max.z),
),
})
}
/// Returns true if this box contains the interior of the other box.
#[inline]
pub fn contains_box(&self, other: &Self) -> bool {
self.min.x <= other.min.x
&& other.max.x <= self.max.x
&& self.min.y <= other.min.y
&& other.max.y <= self.max.y
&& self.min.z <= other.min.z
&& other.max.z <= self.max.z
}
}
impl<T, U> NonEmpty<Box3D<T, U>>
where
T: Copy + Add<T, Output = T>,
{
#[inline]
pub fn translate(&self, by: Vector3D<T, U>) -> Self {
NonEmpty(self.0.translate(by))
}
}
#[test]
fn empty_nonempty() {
use default;
// zero-width
let box1: default::Box2D<i32> = Box2D {
min: point2(-10, 2),
max: point2(-10, 12),
};
// zero-height
let box2: default::Box2D<i32> = Box2D {
min: point2(0, 11),
max: point2(2, 11),
};
// negative width
let box3: default::Box2D<i32> = Box2D {
min: point2(1, 11),
max: point2(0, 12),
};
// negative height
let box4: default::Box2D<i32> = Box2D {
min: point2(0, 11),
max: point2(5, 10),
};
assert!(box1.to_non_empty().is_none());
assert!(box2.to_non_empty().is_none());
assert!(box3.to_non_empty().is_none());
assert!(box4.to_non_empty().is_none());
}
#[test]
fn nonempty_union() {
use default;
let box1: default::Box2D<i32> = Box2D {
min: point2(-10, 2),
max: point2(15, 12),
};
let box2 = Box2D {
min: point2(-2, -5),
max: point2(10, 5),
};
assert_eq!(box1.union(&box2), *box1.to_non_empty().unwrap().union(&box2.to_non_empty().unwrap()));
let box3: default::Box3D<i32> = Box3D {
min: point3(1, -10, 2),
max: point3(6, 15, 12),
};
let box4 = Box3D {
min: point3(0, -2, -5),
max: point3(7, 10, 5),
};
assert_eq!(box3.union(&box4), *box3.to_non_empty().unwrap().union(&box4.to_non_empty().unwrap()));
let rect1: default::Rect<i32> = Rect {
origin: point2(1, 2),
size: size2(3, 4),
};
let rect2 = Rect {
origin: point2(-1, 5),
size: size2(1, 10),
};
assert_eq!(rect1.union(&rect2), *rect1.to_non_empty().unwrap().union(&rect2.to_non_empty().unwrap()));
}
#[test]
fn nonempty_contains() {
use default;
use {vec2, vec3};
let r: NonEmpty<default::Rect<i32>> = Rect {
origin: point2(-20, 15),
size: size2(100, 200),
}.to_non_empty().unwrap();
assert!(r.contains_rect(&r));
assert!(!r.contains_rect(&r.translate(vec2(1, 0))));
assert!(!r.contains_rect(&r.translate(vec2(-1, 0))));
assert!(!r.contains_rect(&r.translate(vec2(0, 1))));
assert!(!r.contains_rect(&r.translate(vec2(0, -1))));
let b: NonEmpty<default::Box2D<i32>> = Box2D {
min: point2(-10, 5),
max: point2(30, 100),
}.to_non_empty().unwrap();
assert!(b.contains_box(&b));
assert!(!b.contains_box(&b.translate(vec2(1, 0))));
assert!(!b.contains_box(&b.translate(vec2(-1, 0))));
assert!(!b.contains_box(&b.translate(vec2(0, 1))));
assert!(!b.contains_box(&b.translate(vec2(0, -1))));
let b: NonEmpty<default::Box3D<i32>> = Box3D {
min: point3(-1, -10, 5),
max: point3(10, 30, 100),
}.to_non_empty().unwrap();
assert!(b.contains_box(&b));
assert!(!b.contains_box(&b.translate(vec3(0, 1, 0))));
assert!(!b.contains_box(&b.translate(vec3(0, -1, 0))));
assert!(!b.contains_box(&b.translate(vec3(0, 0, 1))));
assert!(!b.contains_box(&b.translate(vec3(0, 0, -1))));
assert!(!b.contains_box(&b.translate(vec3(1, 1, 0))));
assert!(!b.contains_box(&b.translate(vec3(1, -1, 0))));
assert!(!b.contains_box(&b.translate(vec3(1, 0, 1))));
assert!(!b.contains_box(&b.translate(vec3(1, 0, -1))));
assert!(!b.contains_box(&b.translate(vec3(-1, 1, 0))));
assert!(!b.contains_box(&b.translate(vec3(-1, -1, 0))));
assert!(!b.contains_box(&b.translate(vec3(-1, 0, 1))));
assert!(!b.contains_box(&b.translate(vec3(-1, 0, -1))));
}