use crate::Point;
#[derive(Debug, Clone, Copy, PartialEq)]
#[repr(C)]
pub struct Rect {
pub min: Point,
pub max: Point,
}
impl Rect {
#[inline]
pub const fn new(min: Point, max: Point) -> Self {
Self { min, max }
}
#[inline]
pub const fn from_coords(min_x: f64, min_y: f64, max_x: f64, max_y: f64) -> Self {
Self {
min: Point::new(min_x, min_y),
max: Point::new(max_x, max_y),
}
}
#[inline]
pub fn center(self) -> Point {
let p = unsafe { tg_geom_sys::tg_rect_center(self.into()) };
p.into()
}
#[inline]
pub fn expand(self, other: Rect) -> Rect {
let r = unsafe { tg_geom_sys::tg_rect_expand(self.into(), other.into()) };
r.into()
}
#[inline]
pub fn expand_point(self, point: Point) -> Rect {
let r = unsafe { tg_geom_sys::tg_rect_expand_point(self.into(), point.into()) };
r.into()
}
#[inline]
pub fn intersects_rect(self, other: Rect) -> bool {
unsafe { tg_geom_sys::tg_rect_intersects_rect(self.into(), other.into()) }
}
#[inline]
pub fn intersects_point(self, point: Point) -> bool {
unsafe { tg_geom_sys::tg_rect_intersects_point(self.into(), point.into()) }
}
#[inline]
pub fn width(self) -> f64 {
self.max.x - self.min.x
}
#[inline]
pub fn height(self) -> f64 {
self.max.y - self.min.y
}
#[inline]
pub fn area(self) -> f64 {
self.width() * self.height()
}
}
impl From<tg_geom_sys::tg_rect> for Rect {
#[inline]
fn from(r: tg_geom_sys::tg_rect) -> Self {
Self {
min: r.min.into(),
max: r.max.into(),
}
}
}
impl From<Rect> for tg_geom_sys::tg_rect {
#[inline]
fn from(r: Rect) -> Self {
Self {
min: r.min.into(),
max: r.max.into(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn p(x: f64, y: f64) -> Point {
Point::new(x, y)
}
fn r(min_x: f64, min_y: f64, max_x: f64, max_y: f64) -> Rect {
Rect::from_coords(min_x, min_y, max_x, max_y)
}
#[test]
fn test_rect_center() {
assert_eq!(r(0.0, 0.0, 10.0, 10.0).center(), p(5.0, 5.0));
assert_eq!(r(0.0, 0.0, 0.0, 0.0).center(), p(0.0, 0.0));
assert_eq!(r(-10.0, -10.0, 10.0, 10.0).center(), p(0.0, 0.0));
}
#[test]
fn test_rect_intersects_rect() {
assert!(r(0.0, 0.0, 10.0, 10.0).intersects_rect(r(0.0, 0.0, 10.0, 10.0)));
assert!(r(0.0, 0.0, 10.0, 10.0).intersects_rect(r(2.0, 2.0, 8.0, 8.0)));
assert!(r(0.0, 0.0, 10.0, 10.0).intersects_rect(r(-1.0, 0.0, 10.0, 10.0)));
assert!(r(0.0, 0.0, 10.0, 10.0).intersects_rect(r(0.0, -1.0, 10.0, 10.0)));
assert!(r(0.0, 0.0, 10.0, 10.0).intersects_rect(r(0.0, 0.0, 11.0, 10.0)));
assert!(r(0.0, 0.0, 10.0, 10.0).intersects_rect(r(0.0, 0.0, 10.0, 11.0)));
assert!(!r(0.0, 0.0, 10.0, 10.0).intersects_rect(r(11.0, 0.0, 21.0, 10.0)));
assert!(!r(0.0, 0.0, 10.0, 10.0).intersects_rect(r(0.0, 11.0, 10.0, 21.0)));
assert!(!r(0.0, 0.0, 10.0, 10.0).intersects_rect(r(11.0, 11.0, 21.0, 21.0)));
assert!(!r(0.0, 0.0, 10.0, 10.0).intersects_rect(r(-11.0, -11.0, -1.0, -1.0)));
}
#[test]
fn test_rect_intersects_point() {
assert!(r(0.0, 0.0, 10.0, 10.0).intersects_point(p(5.0, 5.0)));
assert!(!r(0.0, 0.0, 10.0, 10.0).intersects_point(p(15.0, 15.0)));
assert!(r(0.0, 0.0, 10.0, 10.0).intersects_point(p(0.0, 0.0)));
assert!(r(0.0, 0.0, 10.0, 10.0).intersects_point(p(5.0, 0.0)));
}
#[test]
fn test_rect_new() {
let rect = Rect::new(p(1.0, 2.0), p(3.0, 4.0));
assert_eq!(rect.min, p(1.0, 2.0));
assert_eq!(rect.max, p(3.0, 4.0));
}
#[test]
fn test_rect_from_coords() {
let rect = Rect::from_coords(1.0, 2.0, 3.0, 4.0);
assert_eq!(rect.min, p(1.0, 2.0));
assert_eq!(rect.max, p(3.0, 4.0));
}
#[test]
fn test_rect_width_height_area() {
let rect = r(1.0, 2.0, 5.0, 8.0);
assert!((rect.width() - 4.0).abs() < 1e-10);
assert!((rect.height() - 6.0).abs() < 1e-10);
assert!((rect.area() - 24.0).abs() < 1e-10);
let h_line = r(0.0, 5.0, 10.0, 5.0);
assert_eq!(h_line.height(), 0.0);
assert_eq!(h_line.area(), 0.0);
let v_line = r(5.0, 0.0, 5.0, 10.0);
assert_eq!(v_line.width(), 0.0);
assert_eq!(v_line.area(), 0.0);
let point_rect = r(5.0, 5.0, 5.0, 5.0);
assert_eq!(point_rect.width(), 0.0);
assert_eq!(point_rect.height(), 0.0);
assert_eq!(point_rect.area(), 0.0);
}
#[test]
fn test_rect_expand() {
let r1 = r(0.0, 0.0, 1.0, 1.0);
let r2 = r(2.0, 2.0, 3.0, 3.0);
let expanded = r1.expand(r2);
assert_eq!(expanded.min, p(0.0, 0.0));
assert_eq!(expanded.max, p(3.0, 3.0));
let r3 = r(-2.0, -2.0, -1.0, -1.0);
let expanded2 = r1.expand(r3);
assert_eq!(expanded2.min, p(-2.0, -2.0));
assert_eq!(expanded2.max, p(1.0, 1.0));
assert_eq!(r1.expand(r1), r1);
}
#[test]
fn test_rect_expand_point() {
let rect = r(0.0, 0.0, 1.0, 1.0);
let expanded = rect.expand_point(p(5.0, 5.0));
assert_eq!(expanded.max, p(5.0, 5.0));
let r2 = r(0.0, 0.0, 10.0, 10.0);
let expanded2 = r2.expand_point(p(5.0, 5.0));
assert_eq!(expanded2, r2);
let expanded3 = rect.expand_point(p(-5.0, -5.0));
assert_eq!(expanded3.min, p(-5.0, -5.0));
}
#[test]
fn test_rect_sys_roundtrip() {
let rect = r(1.0, 2.0, 3.0, 4.0);
let sys_rect: tg_geom_sys::tg_rect = rect.into();
let back: Rect = sys_rect.into();
assert_eq!(rect, back);
}
#[test]
fn test_rect_eq() {
assert_eq!(r(0.0, 0.0, 1.0, 1.0), r(0.0, 0.0, 1.0, 1.0));
assert_ne!(r(0.0, 0.0, 1.0, 1.0), r(0.0, 0.0, 2.0, 2.0));
}
#[test]
fn test_rect_debug() {
let rect = r(1.0, 2.0, 3.0, 4.0);
let debug_str = format!("{rect:?}");
assert!(debug_str.contains("Rect"));
}
#[test]
fn test_rect_negative() {
let rect = r(-10.0, -10.0, -5.0, -5.0);
assert_eq!(rect.center(), p(-7.5, -7.5));
assert!(rect.intersects_point(p(-7.5, -7.5)));
assert!(!rect.intersects_point(p(0.0, 0.0)));
}
}