use super::Vec2; use nalgebra::{RealField, Scalar}; use serde::{Deserialize, Serialize}; use std::ops::{Add, AddAssign}; /// Represents a Rectangle with the value type T. #[derive(Copy, Clone, Debug, Default, Serialize, Deserialize)] pub struct Rect { /// The x coordinate, or leftmost coordinate of the Rect. pub x: T, /// The y coordinate, or rightmost coordinate of the Rect. pub y: T, /// The width of the Rect. pub w: T, /// The height of the Rect. pub h: T, } // This is sad, but also sadly necessary :/ impl + Scalar + Copy> Into for Rect { fn into(self) -> raylib::ffi::Rectangle { raylib::ffi::Rectangle { x: self.x.into(), y: self.y.into(), width: self.w.into(), height: self.h.into(), } } } impl + Scalar + Copy> From for Rect { fn from(r: raylib::ffi::Rectangle) -> Self { Self { x: T::from(r.x), y: T::from(r.y), w: T::from(r.width), h: T::from(r.height), } } } impl + Scalar + Copy> Into for Rect { fn into(self) -> raylib::math::Rectangle { raylib::math::Rectangle { x: self.x.into(), y: self.y.into(), width: self.w.into(), height: self.h.into(), } } } impl + Scalar + Copy> From for Rect { fn from(r: raylib::math::Rectangle) -> Self { Self { x: T::from(r.x), y: T::from(r.y), w: T::from(r.width), h: T::from(r.height), } } } impl Rect { pub fn new(x: T, y: T, w: T, h: T) -> Self { Self { x, y, w, h } } /// Create a Rectangle from a slice. Indices are [x, y, w, h]. pub fn from_slice(slice: [T; 4]) -> Rect where T: Copy, { Rect { x: slice[0], y: slice[1], w: slice[2], h: slice[3], } } /// Move by the Vec provided. pub fn translate(&mut self, by: Vec2) where T: AddAssign, { self.x += by.x; self.y += by.y; } /// Set the posiotien of the rectangle to the given one without changing its /// size pub fn set_pos(&mut self, pos: Vec2) { self.x = pos.x; self.y = pos.y; } /// Test if two rectangles intersect. pub fn intersect<'a>(this: &'a Rect, other: &'a Rect) -> bool where T: Add + PartialOrd + Copy, { !(this.x > other.x + other.w || this.x + this.w < other.x || this.y > other.y + other.h || this.y + this.h < other.y) } /// Check if the point is inside this Rect and return true if so. pub fn contains(&self, point: Vec2) -> bool where T: PartialOrd + Add, { point.x >= self.x && point.x <= self.x + self.w && point.y >= self.y && point.y <= self.y + self.h } /// Function to calculate the bounding rectangle that is between two vectors. The order of the /// vectors is irrelevent for this. As long as they are diagonally opposite of each other, this /// function will work. pub fn bounding_rect(pos1: Vec2, pos2: Vec2) -> Self where T: RealField, { let min_x = pos1.x.min(pos2.x); let min_y = pos1.y.min(pos2.y); let max_x = pos1.x.max(pos2.x); let max_y = pos1.y.max(pos2.y); Self { x: min_x, y: min_y, w: max_x - min_x, h: max_y - min_y, } } /// Get the shortest way that must be applied to this Rect to clear out of /// another Rect of the same type so that they would not intersect any more. pub fn shortest_way_out(&self, of: &Rect) -> Vec2 where T: RealField, { // Check upwards let mut move_y = of.y - self.y - self.h; // Check downwards let move_down = of.y + of.h - self.y; if move_down < -move_y { move_y = move_down; } // Check left let mut move_x = of.x - self.x - self.w; // Check right let move_right = of.x + of.w - self.x; if move_right < -move_x { move_x = move_right; } if move_x.abs() < move_y.abs() { Vec2::new(move_x, T::zero()) } else { Vec2::new(T::zero(), move_y) } } } #[cfg(test)] mod test { use super::*; #[test] fn test_intersect() { let a = Rect::from_slice([0, 0, 4, 4]); let b = Rect::from_slice([-1, -1, 1, 1]); assert!(Rect::intersect(&a, &b)); } }