orinium_browser/engine/renderer_model/
geom.rs1#[derive(Debug, Clone, Copy)]
6pub struct AffineTransform {
7 pub m11: f32,
8 pub m12: f32,
9 pub m21: f32,
10 pub m22: f32,
11 pub dx: f32,
12 pub dy: f32,
13}
14
15impl AffineTransform {
16 pub const fn identity() -> Self {
18 AffineTransform {
19 m11: 1.0,
20 m12: 0.0,
21 m21: 0.0,
22 m22: 1.0,
23 dx: 0.0,
24 dy: 0.0,
25 }
26 }
27
28 pub fn translate(dx: f32, dy: f32) -> Self {
30 AffineTransform {
31 m11: 1.0,
32 m12: 0.0,
33 m21: 0.0,
34 m22: 1.0,
35 dx,
36 dy,
37 }
38 }
39
40 pub const fn scale(sx: f32, sy: f32) -> Self {
42 AffineTransform {
43 m11: sx,
44 m12: 0.0,
45 m21: 0.0,
46 m22: sy,
47 dx: 0.0,
48 dy: 0.0,
49 }
50 }
51
52 pub fn rotate(angle: f32) -> Self {
54 let c = angle.cos();
55 let s = angle.sin();
56 AffineTransform {
57 m11: c,
58 m12: -s,
59 m21: s,
60 m22: c,
61 dx: 0.0,
62 dy: 0.0,
63 }
64 }
65
66 pub fn apply(&self, x: f32, y: f32) -> (f32, f32) {
68 (
69 x * self.m11 + y * self.m12 + self.dx,
70 x * self.m21 + y * self.m22 + self.dy,
71 )
72 }
73
74 pub fn then(&self, rhs: &AffineTransform) -> Self {
76 AffineTransform {
77 m11: self.m11 * rhs.m11 + self.m12 * rhs.m21,
78 m12: self.m11 * rhs.m12 + self.m12 * rhs.m22,
79 m21: self.m21 * rhs.m11 + self.m22 * rhs.m21,
80 m22: self.m21 * rhs.m12 + self.m22 * rhs.m22,
81 dx: self.m11 * rhs.dx + self.m12 * rhs.dy + self.dx,
82 dy: self.m21 * rhs.dx + self.m22 * rhs.dy + self.dy,
83 }
84 }
85
86 pub fn inverse(&self) -> Option<Self> {
88 let det = self.m11 * self.m22 - self.m12 * self.m21;
89 if det.abs() < f32::EPSILON {
90 return None;
91 }
92 let inv_det = 1.0 / det;
93 Some(AffineTransform {
94 m11: self.m22 * inv_det,
95 m12: -self.m12 * inv_det,
96 m21: -self.m21 * inv_det,
97 m22: self.m11 * inv_det,
98 dx: (self.m21 * self.dy - self.m22 * self.dx) * inv_det,
99 dy: (self.m12 * self.dx - self.m11 * self.dy) * inv_det,
100 })
101 }
102}
103
104#[derive(Debug, Clone, Copy)]
106pub struct Rect {
107 pub x: f32,
108 pub y: f32,
109 pub width: f32,
110 pub height: f32,
111}
112
113impl Rect {
114 pub const fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
116 Self {
117 x,
118 y,
119 width,
120 height,
121 }
122 }
123
124 pub fn contains(&self, x: f32, y: f32) -> bool {
126 x >= self.x && x <= self.x + self.width && y >= self.y && y <= self.y + self.height
127 }
128
129 pub fn intersect(&self, other: &Rect) -> Option<Rect> {
132 let x1 = self.x.max(other.x);
133 let y1 = self.y.max(other.y);
134 let x2 = (self.x + self.width).min(other.x + other.width);
135 let y2 = (self.y + self.height).min(other.y + other.height);
136 if x2 > x1 && y2 > y1 {
137 Some(Rect::new(x1, y1, x2 - x1, y2 - y1))
138 } else {
139 None
140 }
141 }
142}
143
144#[cfg(test)]
145mod tests {
146 use super::*;
147
148 #[test]
149 fn test_affine_transform_composition_and_inverse() {
150 let t = AffineTransform::translate(3.0, 4.0).then(&AffineTransform::translate(5.0, 6.0));
151 assert_eq!(t.apply(1.0, 1.0), (9.0, 11.0));
152
153 let inv = t.inverse().unwrap();
154 let (x, y) = t.apply(10.0, 20.0);
155 let (rx, ry) = inv.apply(x, y);
156 assert!((rx - 10.0).abs() < 1e-5, "rx={rx}");
157 assert!((ry - 20.0).abs() < 1e-5, "ry={ry}");
158 }
159
160 #[test]
161 fn test_affine_transform_scale_rotate() {
162 let t = AffineTransform::scale(2.0, 3.0)
164 .then(&AffineTransform::rotate(std::f32::consts::FRAC_PI_2));
165 let (x, y) = t.apply(1.0, 0.0);
166 assert!((x - 0.0).abs() < 1e-5, "x={x}");
167 assert!((y - 3.0).abs() < 1e-5, "y={y}");
168 }
169
170 #[test]
171 fn test_rect_intersect() {
172 let a = Rect::new(0.0, 0.0, 10.0, 10.0);
173 let b = Rect::new(5.0, 5.0, 10.0, 10.0);
174 let i = a.intersect(&b).unwrap();
175 assert!((i.x - 5.0).abs() < 1e-6);
176 assert!((i.width - 5.0).abs() < 1e-6);
177 assert!(Rect::new(20.0, 20.0, 1.0, 1.0).intersect(&a).is_none());
178 }
179}