use super::{LineSegment, Polygon, Surface, Vec2}; //use alga::general::{Additive, Identity}; use nalgebra::{ClosedAdd, ClosedSub, RealField, Scalar}; use num_traits::identities::Zero; use num_traits::{NumCast, ToPrimitive}; 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, } 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) } /// 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, } } /// Function to calculate the bounding rectangle of n vertices provided. The order of them is /// not relevant and a point that is contained by the vertices will not change the result. /// /// # Panics /// If there is not at least one vertex in the vertices slice, the function will panic, since it /// is impossible to calculate any bounds in such a case. pub fn bounding_rect_n(vertices: &[Vec2]) -> Self where T: RealField, { if vertices.is_empty() { panic!("Cannot create bounding rectangle without any vertices"); } let mut min = vertices[0]; let mut max = vertices[1]; for vertex in vertices.iter().skip(1) { min.x = super::partial_min(min.x, vertex.x); min.y = super::partial_min(min.y, vertex.y); max.x = super::partial_max(max.x, vertex.x); max.y = super::partial_max(max.y, vertex.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) } } } impl Surface for Rect { fn contains_point(&self, point: &Vec2) -> bool { point.x >= self.x && point.x <= self.x + self.w && point.y >= self.y && point.y <= self.y + self.h } fn contains_line_segment(&self, line_segment: &LineSegment) -> bool { self.contains_point(&line_segment.start) && self.contains_point(&line_segment.end) } fn contains_rect(&self, rect: &Rect) -> bool { /* True, if the rightmost x-coordinate of the called rectangle is further right or the same * as the rightmost coordinate of the given rect. */ let x_exceeds = self.x + self.w - rect.x - rect.w >= T::zero(); // The same for the y-coordinates let y_exceeds = self.y + self.h - rect.y - rect.h >= T::zero(); x_exceeds && y_exceeds && self.x <= rect.x && self.y <= rect.y } fn contains_polygon(&self, polygon: &Polygon) -> bool { // Check if all vertices of the polygon lie inside the rectangle. If so, the polygon must // be contained in its entirety. polygon .corners() .iter() .all(|&corner| self.contains_point(&corner)) } fn is_inside_rect(&self, rect: &Rect) -> bool { rect.contains_rect(&self) } } // This is sad, but also sadly necessary :/ 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> 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 Into for Rect { fn into(self) -> raylib::math::Rectangle { raylib::math::Rectangle { x: NumCast::from(self.x).expect("Unable to cast Rect into raylib Rect"), y: NumCast::from(self.y).expect("Unable to cast Rect into raylib Rect"), width: NumCast::from(self.w).expect("Unable to cast Rect into raylib Rect"), height: NumCast::from(self.h).expect("Unable to cast Rect into raylib Rect"), } } } impl Into for Rect { fn into(self) -> raylib::ffi::Rectangle { raylib::ffi::Rectangle { x: NumCast::from(self.x).expect("Unable to cast Rect into raylib Rect"), y: NumCast::from(self.y).expect("Unable to cast Rect into raylib Rect"), width: NumCast::from(self.w).expect("Unable to cast Rect into raylib Rect"), height: NumCast::from(self.h).expect("Unable to cast Rect into raylib Rect"), } } } #[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)); } }