summaryrefslogtreecommitdiff
path: root/src/rect.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/rect.rs')
-rw-r--r--src/rect.rs193
1 files changed, 193 insertions, 0 deletions
diff --git a/src/rect.rs b/src/rect.rs
new file mode 100644
index 0000000..3f5e9e3
--- /dev/null
+++ b/src/rect.rs
@@ -0,0 +1,193 @@
+use crate::*;
+use std::fmt;
+use std::ops::{Add, Sub, AddAssign, SubAssign};
+
+#[derive(Clone, Copy, PartialEq)]
+pub struct Rect<O: Internal, D: Internal> {
+ pub origin: Point<O>,
+ pub dimensions: Dimensions<D>,
+}
+
+pub trait HasRect<O: Internal, D: Internal>: HasPosition<O> + HasDimensions<D> {
+ fn rect(&self) -> Rect<O, D>;
+}
+
+impl<O: Internal, D: Internal> Rect<O, D> {
+ pub const ZERO: Rect<O, D> = Rect::construct(Point::<O>::ZERO, Dimensions::<D>::ZERO);
+
+ pub const fn new(origin_x: O, origin_y: O, width: D, height: D) -> Self {
+ let origin = Point::new(origin_x, origin_y);
+ let dimensions = Dimensions::new(width, height);
+ Self { origin, dimensions }
+ }
+
+ pub const fn construct(origin: Point<O>, dimensions: Dimensions<D>) -> Self {
+ Self { origin, dimensions }
+ }
+}
+
+impl<O: Internal, D: Internal> HasDimensions<D> for Rect<O, D> {
+ fn dimensions(&self) -> Dimensions<D> { self.dimensions }
+}
+
+impl<O: Internal, D: Internal> HasPosition<O> for Rect<O, D> {
+ fn position(&self) -> Point<O> { self.origin }
+}
+
+impl<O: Internal, D: Internal, T: HasDimensions<D> + HasPosition<O>> HasRect<O, D> for T {
+ fn rect(&self) -> Rect<O, D> {
+ Rect::construct(self.position(), self.dimensions())
+ }
+}
+
+impl<O:Internal, D: Internal> Add<Point<O>> for Rect<O, D> {
+ type Output = Rect<O,D>;
+ fn add(self, point: Point<O>) -> Self::Output {
+ Rect::construct(self.origin + point, self.dimensions)
+ }
+}
+
+impl<O: Internal, D: Internal> Sub<Point<O>> for Rect<O, D> {
+ type Output = Rect<O,D>;
+ fn sub(self, point: Point<O>) -> Self::Output {
+ Rect::construct(self.origin - point, self.dimensions)
+ }
+}
+
+impl<O: Internal, D: Internal> AddAssign<Point<O>> for Rect<O, D> {
+ fn add_assign(&mut self, point: Point<O>) {
+ self.origin += point
+ }
+}
+
+impl<O: Internal, D: Internal> SubAssign<Point<O>> for Rect<O, D> {
+ fn sub_assign(&mut self, point: Point<O>) {
+ self.origin -= point
+ }
+}
+
+impl<O: Internal, D: Internal> fmt::Debug for Rect<O, D> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "Rect {{ x:{:?} y:{:?} w:{:?} h:{:?} }}",
+ self.origin.x, self.origin.y,
+ self.dimensions.width, self.dimensions.height,
+ )
+ }
+}
+
+// -----------------------------------------------------------------------------
+// Macro-generated code
+
+// PartialOrd versions of std::cmp::min/max
+fn min<T: std::cmp::PartialOrd>(a: T, b: T) -> T {
+ match a < b { true => a, false => b }
+}
+fn max<T: std::cmp::PartialOrd>(a: T, b: T) -> T {
+ match a > b { true => a, false => b }
+}
+
+macro_rules! impl_for_types {
+ ($O:ty, $D:ty) => {
+impl Rect<$O, $D> {
+ pub const fn from_dimensions(dimensions: Dimensions<$D>) -> Self {
+ Self {
+ origin: Point::<$O>::ZERO,
+ dimensions,
+ }
+ }
+
+ pub fn from_points(point1: Point<$O>, point2: Point<$O>) -> Self {
+ let (left, right) = match point1.x < point2.x {
+ true => (point1.x, point2.x),
+ false => (point2.x, point1.x),
+ };
+ let (top, bottom) = match point1.y < point2.y {
+ true => (point1.y, point2.y),
+ false => (point2.y, point1.y),
+ };
+ let width = (right - left) as $D;
+ let height = (bottom - top) as $D;
+
+ Self {
+ origin: Point::new(left, top),
+ dimensions: Dimensions::new(width, height),
+ }
+ }
+
+ /// Returns Rect(0,0,0,0) where no intersection exists.
+ pub fn intersect(&self, other: Rect<$O, $D>) -> Self {
+ let left = max(self.origin.x, other.origin.x);
+ let top = max(self.origin.y, other.origin.y);
+
+ let right_self = self.origin.x + (self.width() as $O);
+ let right_other = other.origin.x + (other.width() as $O);
+ let bottom_self = self.origin.y + (self.height() as $O);
+ let bottom_other = other.origin.y + (other.height() as $O);
+ let right = min(right_self, right_other);
+ let bottom = min(bottom_self, bottom_other);
+
+ if right <= left || bottom <= top {
+ Rect::<$O, $D>::ZERO
+ } else {
+ let width = (right - left) as $D;
+ let height = (bottom - top) as $D;
+ Rect::construct(Point::new(left, top), Dimensions::new(width, height))
+ }
+ }
+
+ pub fn contains_point(&self, point: Point<$O>) -> bool {
+ let left = self.origin.x;
+ let top = self.origin.y;
+ let right = left + (self.width() as $O);
+ let bottom = top + (self.height() as $O);
+ point.x >= left && point.x < right && point.y >= top && point.y < bottom
+ }
+
+ pub fn include_point(&mut self, _point: Point<$O>) {
+ todo!()
+ }
+
+ /// Return the inclusive left x-coordinate.
+ pub fn left(&self) -> $O {
+ self.origin.x
+ }
+
+ /// Return the inclusive top y-coordinate.
+ pub fn top(&self) -> $O {
+ self.origin.y
+ }
+
+ /// Return the exclusive right x-coordinate.
+ pub fn right(&self) -> $O {
+ self.left() + self.width() as $O
+ }
+ /// Return the exclusive bottom y-coordinate.
+ pub fn bottom(&self) -> $O {
+ self.top() + self.height() as $O
+ }
+}
+
+impl From<Dimensions<$D>> for Rect<$O, $D> {
+ fn from(dimensions: Dimensions<$D>) -> Rect<$O, $D> {
+ Rect::<$O, $D>::from_dimensions(dimensions)
+ }
+}
+ };
+}
+
+impl_for_types!(u8, u8);
+impl_for_types!(u16, u16);
+impl_for_types!(u32, u32);
+impl_for_types!(u64, u64);
+impl_for_types!(u128, u128);
+impl_for_types!(usize, usize);
+
+impl_for_types!(i8, u8);
+impl_for_types!(i16, u16);
+impl_for_types!(i32, u32);
+impl_for_types!(i64, u64);
+impl_for_types!(i128, u128);
+impl_for_types!(isize, usize);
+
+impl_for_types!(f32, f32);
+impl_for_types!(f64, f64);