#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Rect {
pub x0: f32,
pub y0: f32,
pub x1: f32,
pub y1: f32,
}
impl Rect {
pub const ZERO: Self = Self {
x0: 0.0,
y0: 0.0,
x1: 0.0,
y1: 0.0,
};
pub fn new(x0: f32, y0: f32, x1: f32, y1: f32) -> Self {
Self {
x0: x0.min(x1),
y0: y0.min(y1),
x1: x0.max(x1),
y1: y0.max(y1),
}
}
pub fn width(&self) -> f32 {
self.x1 - self.x0
}
pub fn height(&self) -> f32 {
self.y1 - self.y0
}
pub fn area(&self) -> f32 {
self.width() * self.height()
}
pub fn union(self, other: Rect) -> Rect {
Rect {
x0: self.x0.min(other.x0),
y0: self.y0.min(other.y0),
x1: self.x1.max(other.x1),
y1: self.y1.max(other.y1),
}
}
pub fn contains_point(&self, x: f32, y: f32) -> bool {
x >= self.x0 && x <= self.x1 && y >= self.y0 && y <= self.y1
}
pub fn intersects(&self, other: &Rect) -> bool {
self.x0 <= other.x1 && self.x1 >= other.x0 && self.y0 <= other.y1 && self.y1 >= other.y0
}
pub fn flip_y(self, page_height: f32) -> Rect {
Rect {
x0: self.x0,
x1: self.x1,
y0: page_height - self.y1,
y1: page_height - self.y0,
}
}
}
impl From<Rect> for (f32, f32, f32, f32) {
fn from(r: Rect) -> Self {
(r.x0, r.y0, r.x1, r.y1)
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Matrix {
pub a: f32,
pub b: f32,
pub c: f32,
pub d: f32,
pub e: f32,
pub f: f32,
}
impl Matrix {
pub const IDENTITY: Self = Self {
a: 1.0,
b: 0.0,
c: 0.0,
d: 1.0,
e: 0.0,
f: 0.0,
};
pub fn new(a: f32, b: f32, c: f32, d: f32, e: f32, f: f32) -> Self {
Self { a, b, c, d, e, f }
}
pub fn translation(tx: f32, ty: f32) -> Self {
Self {
a: 1.0,
b: 0.0,
c: 0.0,
d: 1.0,
e: tx,
f: ty,
}
}
pub fn premultiply(self, other: Matrix) -> Matrix {
Matrix {
a: other.a * self.a + other.b * self.c,
b: other.a * self.b + other.b * self.d,
c: other.c * self.a + other.d * self.c,
d: other.c * self.b + other.d * self.d,
e: other.e * self.a + other.f * self.c + self.e,
f: other.e * self.b + other.f * self.d + self.f,
}
}
pub fn transform_point(&self, x: f32, y: f32) -> (f32, f32) {
(
self.a * x + self.c * y + self.e,
self.b * x + self.d * y + self.f,
)
}
pub fn scale_x(&self) -> f32 {
(self.a * self.a + self.b * self.b).sqrt()
}
pub fn scale_y(&self) -> f32 {
(self.c * self.c + self.d * self.d).sqrt()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn rect_normalizes_inverted_input() {
let r = Rect::new(10.0, 20.0, 0.0, 5.0);
assert_eq!(r.x0, 0.0);
assert_eq!(r.x1, 10.0);
assert_eq!(r.y0, 5.0);
assert_eq!(r.y1, 20.0);
}
#[test]
fn rect_union_grows_bbox() {
let a = Rect::new(0.0, 0.0, 10.0, 10.0);
let b = Rect::new(5.0, -5.0, 15.0, 5.0);
let u = a.union(b);
assert_eq!(u, Rect::new(0.0, -5.0, 15.0, 10.0));
}
#[test]
fn matrix_identity_round_trip() {
let m = Matrix::IDENTITY;
assert_eq!(m.transform_point(3.0, 4.0), (3.0, 4.0));
}
#[test]
fn matrix_translation_premultiply() {
let t = Matrix::translation(10.0, 20.0);
assert_eq!(t.transform_point(0.0, 0.0), (10.0, 20.0));
let t2 = t.premultiply(Matrix::translation(1.0, 2.0));
assert_eq!(t2.transform_point(0.0, 0.0), (11.0, 22.0));
}
}