lumecs 0.0.1

Experimental GUI backed by Bevy ECS + usual suspects
use std::ops::{Div, Mul};

use crate::point::Point;

#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Rectangle {
    pub top: f32,
    pub right: f32,
    pub bottom: f32,
    pub left: f32,
}

impl Rectangle {
    pub fn zero() -> Rectangle {
        Rectangle {
            top: 0.0,
            right: 0.0,
            bottom: 0.0,
            left: 0.0,
        }
    }

    pub fn from_size(width: f32, height: f32) -> Rectangle {
        Rectangle {
            top: 0.0,
            right: width,
            bottom: height,
            left: 0.0,
        }
    }

    pub fn from_xywh(x: f32, y: f32, width: f32, height: f32) -> Rectangle {
        Rectangle {
            top: y,
            right: x + width,
            bottom: y + height,
            left: x,
        }
    }

    pub fn size(&self) -> (f32, f32) {
        (self.width(), self.height())
    }

    pub fn x(&self) -> f32 {
        self.left
    }

    pub fn y(&self) -> f32 {
        self.top
    }

    pub fn width(&self) -> f32 {
        self.right - self.left
    }

    pub fn height(&self) -> f32 {
        self.bottom - self.top
    }

    pub fn contains(&self, x: f32, y: f32) -> bool {
        x >= self.left && x <= self.right && y >= self.top && y <= self.bottom
    }

    pub fn contains_point(&self, point: &Point) -> bool {
        self.contains(point.x, point.y)
    }

    pub fn contains_rectangle(&self, other: &Rectangle) -> bool {
        self.left <= other.left
            && self.right >= other.right
            && self.top <= other.top
            && self.bottom >= other.bottom
    }

    #[must_use]
    pub fn move_by(self, delta: &Point) -> Rectangle {
        Rectangle {
            top: self.top + delta.y,
            right: self.right + delta.x,
            bottom: self.bottom + delta.y,
            left: self.left + delta.x,
        }
    }

    pub fn normalize(&self) -> Rectangle {
        Rectangle {
            top: 0.0,
            right: self.width(),
            bottom: self.height(),
            left: 0.0,
        }
    }

    /// Clip this rectangle to the bounds of another rectangle, returning the resulting rectangle.
    pub fn clamp(&self, other: &Rectangle) -> Rectangle {
        Rectangle {
            top: self.top.max(other.top),
            right: self.right.min(other.right),
            bottom: self.bottom.min(other.bottom),
            left: self.left.max(other.left),
        }
    }

    pub fn clamp_top(&self, other: &Rectangle) -> Rectangle {
        Rectangle {
            top: self.top.max(other.top),
            ..*self
        }
    }

    pub fn clamp_right(&self, other: &Rectangle) -> Rectangle {
        Rectangle {
            right: self.right.min(other.right),
            ..*self
        }
    }

    pub fn clamp_bottom(&self, other: &Rectangle) -> Rectangle {
        Rectangle {
            bottom: self.bottom.min(other.bottom),
            ..*self
        }
    }

    pub fn clamp_left(&self, other: &Rectangle) -> Rectangle {
        Rectangle {
            left: self.left.max(other.left),
            ..*self
        }
    }

    pub fn is_empty(&self) -> bool {
        self.width() <= 0.0 || self.height() <= 0.0
    }
}

impl Div<f32> for Rectangle {
    type Output = Rectangle;

    fn div(self, rhs: f32) -> Self::Output {
        Rectangle {
            top: self.top / rhs,
            right: self.right / rhs,
            bottom: self.bottom / rhs,
            left: self.left / rhs,
        }
    }
}

impl Mul<f32> for Rectangle {
    type Output = Rectangle;

    fn mul(self, rhs: f32) -> Self::Output {
        Rectangle {
            top: self.top * rhs,
            right: self.right * rhs,
            bottom: self.bottom * rhs,
            left: self.left * rhs,
        }
    }
}