image_renderer/
matrix.rs

1//! 变换矩阵
2//!
3//! 提供 2D 仿射变换功能
4
5use crate::point::PointF;
6use crate::rect::{Rect, RectF};
7use std::ops::Mul;
8
9/// 3x3 变换矩阵
10///
11/// 用于 2D 仿射变换,矩阵布局:
12/// ```text
13/// | a  c  e |
14/// | b  d  f |
15/// | 0  0  1 |
16/// ```
17#[derive(Debug, Clone, Copy, PartialEq)]
18pub struct Matrix {
19    /// 矩阵元素,按行优先存储
20    /// [a, b, c, d, e, f] 对应矩阵:
21    /// | a  c  e |
22    /// | b  d  f |
23    /// | 0  0  1 |
24    values: [f32; 6],
25}
26
27impl Matrix {
28    /// 创建单位矩阵
29    pub fn identity() -> Self {
30        Self {
31            values: [1.0, 0.0, 0.0, 1.0, 0.0, 0.0],
32        }
33    }
34
35    /// 创建平移矩阵
36    pub fn translate(dx: f32, dy: f32) -> Self {
37        Self {
38            values: [1.0, 0.0, 0.0, 1.0, dx, dy],
39        }
40    }
41
42    /// 创建缩放矩阵
43    pub fn scale(sx: f32, sy: f32) -> Self {
44        Self {
45            values: [sx, 0.0, 0.0, sy, 0.0, 0.0],
46        }
47    }
48
49    /// 创建旋转矩阵(角度制)
50    pub fn rotate(degrees: f32) -> Self {
51        let radians = degrees.to_radians();
52        let cos = radians.cos();
53        let sin = radians.sin();
54        Self {
55            values: [cos, sin, -sin, cos, 0.0, 0.0],
56        }
57    }
58
59    /// 创建绕指定点旋转的矩阵
60    pub fn rotate_at(degrees: f32, px: f32, py: f32) -> Self {
61        let translate1 = Self::translate(-px, -py);
62        let rotate = Self::rotate(degrees);
63        let translate2 = Self::translate(px, py);
64        translate2 * rotate * translate1
65    }
66
67    /// 创建倾斜矩阵
68    pub fn skew(sx: f32, sy: f32) -> Self {
69        Self {
70            values: [
71                1.0,
72                sy.to_radians().tan(),
73                sx.to_radians().tan(),
74                1.0,
75                0.0,
76                0.0,
77            ],
78        }
79    }
80
81    /// 从数组创建矩阵
82    pub fn from_values(values: [f32; 6]) -> Self {
83        Self { values }
84    }
85
86    /// 获取矩阵元素
87    pub fn values(&self) -> [f32; 6] {
88        self.values
89    }
90
91    /// 获取矩阵的 a 元素(x 缩放)
92    pub fn scale_x(&self) -> f32 {
93        self.values[0]
94    }
95
96    /// 获取矩阵的 b 元素(y 倾斜)
97    pub fn skew_y(&self) -> f32 {
98        self.values[1]
99    }
100
101    /// 获取矩阵的 c 元素(x 倾斜)
102    pub fn skew_x(&self) -> f32 {
103        self.values[2]
104    }
105
106    /// 获取矩阵的 d 元素(y 缩放)
107    pub fn scale_y(&self) -> f32 {
108        self.values[3]
109    }
110
111    /// 获取矩阵的 e 元素(x 平移)
112    pub fn translate_x(&self) -> f32 {
113        self.values[4]
114    }
115
116    /// 获取矩阵的 f 元素(y 平移)
117    pub fn translate_y(&self) -> f32 {
118        self.values[5]
119    }
120
121    /// 应用平移变换
122    pub fn pre_translate(&self, dx: f32, dy: f32) -> Self {
123        Self::translate(dx, dy) * *self
124    }
125
126    /// 应用缩放变换
127    pub fn pre_scale(&self, sx: f32, sy: f32) -> Self {
128        Self::scale(sx, sy) * *self
129    }
130
131    /// 应用旋转变换
132    pub fn pre_rotate(&self, degrees: f32) -> Self {
133        Self::rotate(degrees) * *self
134    }
135
136    /// 后置平移变换
137    pub fn post_translate(&self, dx: f32, dy: f32) -> Self {
138        *self * Self::translate(dx, dy)
139    }
140
141    /// 后置缩放变换
142    pub fn post_scale(&self, sx: f32, sy: f32) -> Self {
143        *self * Self::scale(sx, sy)
144    }
145
146    /// 后置旋转变换
147    pub fn post_rotate(&self, degrees: f32) -> Self {
148        *self * Self::rotate(degrees)
149    }
150
151    /// 变换点(接受 x, y 坐标)
152    pub fn map_point(&self, x: f32, y: f32) -> (f32, f32) {
153        let new_x = self.values[0] * x + self.values[2] * y + self.values[4];
154        let new_y = self.values[1] * x + self.values[3] * y + self.values[5];
155        (new_x, new_y)
156    }
157
158    /// 变换 PointF 对象
159    pub fn map_pointf(&self, point: PointF) -> PointF {
160        let (x, y) = self.map_point(point.x, point.y);
161        PointF::new(x, y)
162    }
163
164    /// 变换多个点
165    pub fn map_points(&self, points: &[PointF]) -> Vec<PointF> {
166        points.iter().map(|&p| self.map_pointf(p)).collect()
167    }
168
169    /// 后置连接矩阵(this * other)
170    pub fn post_concat(&self, other: &Matrix) -> Self {
171        *self * *other
172    }
173
174    /// 变换矩形
175    pub fn map_rect(&self, rect: RectF) -> RectF {
176        let points = [
177            PointF::new(rect.x, rect.y),
178            PointF::new(rect.x + rect.width, rect.y),
179            PointF::new(rect.x + rect.width, rect.y + rect.height),
180            PointF::new(rect.x, rect.y + rect.height),
181        ];
182
183        let transformed = self.map_points(&points);
184
185        let mut min_x = f32::MAX;
186        let mut min_y = f32::MAX;
187        let mut max_x = f32::MIN;
188        let mut max_y = f32::MIN;
189
190        for p in &transformed {
191            min_x = min_x.min(p.x);
192            min_y = min_y.min(p.y);
193            max_x = max_x.max(p.x);
194            max_y = max_y.max(p.y);
195        }
196
197        RectF::new(min_x, min_y, max_x - min_x, max_y - min_y)
198    }
199
200    /// 计算逆矩阵
201    pub fn invert(&self) -> Option<Self> {
202        let [a, b, c, d, e, f] = self.values;
203
204        // 计算行列式
205        let det = a * d - b * c;
206
207        if det.abs() < 1e-10 {
208            return None; // 矩阵不可逆
209        }
210
211        let inv_det = 1.0 / det;
212
213        Some(Self {
214            values: [
215                d * inv_det,
216                -b * inv_det,
217                -c * inv_det,
218                a * inv_det,
219                (c * f - d * e) * inv_det,
220                (b * e - a * f) * inv_det,
221            ],
222        })
223    }
224
225    /// 判断是否为单位矩阵
226    pub fn is_identity(&self) -> bool {
227        const EPSILON: f32 = 1e-6;
228        (self.values[0] - 1.0).abs() < EPSILON
229            && self.values[1].abs() < EPSILON
230            && self.values[2].abs() < EPSILON
231            && (self.values[3] - 1.0).abs() < EPSILON
232            && self.values[4].abs() < EPSILON
233            && self.values[5].abs() < EPSILON
234    }
235
236    /// 判断是否仅包含平移
237    pub fn is_translate(&self) -> bool {
238        const EPSILON: f32 = 1e-6;
239        (self.values[0] - 1.0).abs() < EPSILON
240            && self.values[1].abs() < EPSILON
241            && self.values[2].abs() < EPSILON
242            && (self.values[3] - 1.0).abs() < EPSILON
243    }
244
245    /// 判断是否仅包含缩放和平移
246    pub fn is_scale_translate(&self) -> bool {
247        const EPSILON: f32 = 1e-6;
248        self.values[1].abs() < EPSILON && self.values[2].abs() < EPSILON
249    }
250
251    /// 重置为单位矩阵
252    pub fn reset(&mut self) {
253        *self = Self::identity();
254    }
255
256    /// 设置为平移矩阵
257    pub fn set_translate(&mut self, dx: f32, dy: f32) {
258        *self = Self::translate(dx, dy);
259    }
260
261    /// 设置为缩放矩阵
262    pub fn set_scale(&mut self, sx: f32, sy: f32) {
263        *self = Self::scale(sx, sy);
264    }
265
266    /// 设置为旋转矩阵
267    pub fn set_rotate(&mut self, degrees: f32) {
268        *self = Self::rotate(degrees);
269    }
270}
271
272impl Default for Matrix {
273    fn default() -> Self {
274        Self::identity()
275    }
276}
277
278/// 矩阵乘法
279impl Mul for Matrix {
280    type Output = Self;
281
282    fn mul(self, rhs: Self) -> Self::Output {
283        let [a1, b1, c1, d1, e1, f1] = self.values;
284        let [a2, b2, c2, d2, e2, f2] = rhs.values;
285
286        Self {
287            values: [
288                a1 * a2 + c1 * b2,
289                b1 * a2 + d1 * b2,
290                a1 * c2 + c1 * d2,
291                b1 * c2 + d1 * d2,
292                a1 * e2 + c1 * f2 + e1,
293                b1 * e2 + d1 * f2 + f1,
294            ],
295        }
296    }
297}
298
299/// Canvas 状态
300///
301/// 用于保存和恢复 Canvas 的变换状态
302#[derive(Debug, Clone)]
303pub struct CanvasState {
304    /// 变换矩阵
305    pub matrix: Matrix,
306    /// 裁剪区域(可选)
307    pub clip_rect: Option<Rect>,
308}
309
310impl CanvasState {
311    /// 创建新的状态
312    pub fn new() -> Self {
313        Self {
314            matrix: Matrix::identity(),
315            clip_rect: None,
316        }
317    }
318
319    /// 创建带矩阵的状态
320    pub fn with_matrix(matrix: Matrix) -> Self {
321        Self {
322            matrix,
323            clip_rect: None,
324        }
325    }
326}
327
328impl Default for CanvasState {
329    fn default() -> Self {
330        Self::new()
331    }
332}
333
334#[cfg(test)]
335mod tests {
336    use super::*;
337
338    #[test]
339    fn test_identity_matrix() {
340        let m = Matrix::identity();
341        assert!(m.is_identity());
342        let p = PointF::new(10.0, 20.0);
343        let transformed = m.map_point(p);
344        assert_eq!(transformed, p);
345    }
346
347    #[test]
348    fn test_translate_matrix() {
349        let m = Matrix::translate(10.0, 20.0);
350        let p = PointF::new(5.0, 5.0);
351        let transformed = m.map_point(p);
352        assert_eq!(transformed, PointF::new(15.0, 25.0));
353    }
354
355    #[test]
356    fn test_scale_matrix() {
357        let m = Matrix::scale(2.0, 3.0);
358        let p = PointF::new(10.0, 10.0);
359        let transformed = m.map_point(p);
360        assert_eq!(transformed, PointF::new(20.0, 30.0));
361    }
362
363    #[test]
364    fn test_rotate_matrix() {
365        let m = Matrix::rotate(90.0);
366        let p = PointF::new(1.0, 0.0);
367        let transformed = m.map_point(p);
368        // 旋转 90 度后,(1, 0) 应该变成 (0, 1)
369        assert!((transformed.x - 0.0).abs() < 1e-6);
370        assert!((transformed.y - 1.0).abs() < 1e-6);
371    }
372
373    #[test]
374    fn test_matrix_multiply() {
375        let translate = Matrix::translate(10.0, 20.0);
376        let scale = Matrix::scale(2.0, 2.0);
377        let combined = translate * scale;
378
379        let p = PointF::new(5.0, 5.0);
380        let transformed = combined.map_point(p);
381        // 先缩放再平移:(5*2, 5*2) + (10, 20) = (20, 30)
382        assert_eq!(transformed, PointF::new(20.0, 30.0));
383    }
384
385    #[test]
386    fn test_matrix_invert() {
387        let m = Matrix::translate(10.0, 20.0);
388        let inv = m.invert().unwrap();
389        let combined = m * inv;
390        assert!(combined.is_identity());
391    }
392
393    #[test]
394    fn test_map_rect() {
395        let m = Matrix::scale(2.0, 2.0);
396        let rect = RectF::new(10.0, 10.0, 20.0, 30.0);
397        let transformed = m.map_rect(rect);
398        assert_eq!(transformed.x, 20.0);
399        assert_eq!(transformed.y, 20.0);
400        assert_eq!(transformed.width, 40.0);
401        assert_eq!(transformed.height, 60.0);
402    }
403
404    #[test]
405    fn test_is_translate() {
406        let m = Matrix::translate(10.0, 20.0);
407        assert!(m.is_translate());
408
409        let m = Matrix::scale(2.0, 2.0);
410        assert!(!m.is_translate());
411    }
412
413    #[test]
414    fn test_is_scale_translate() {
415        let m = Matrix::scale(2.0, 3.0);
416        assert!(m.is_scale_translate());
417
418        let m = Matrix::rotate(45.0);
419        assert!(!m.is_scale_translate());
420    }
421}