surpass/
transform.rs

1// Copyright 2022 The Fuchsia Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5use std::error::Error;
6use std::fmt;
7
8use crate::path::MAX_ERROR;
9use crate::{AffineTransform, Point, MAX_HEIGHT, MAX_WIDTH};
10
11const MAX_SCALING_FACTOR_X: f32 = 1.0 + MAX_ERROR as f32 / MAX_WIDTH as f32;
12const MAX_SCALING_FACTOR_Y: f32 = 1.0 + MAX_ERROR as f32 / MAX_HEIGHT as f32;
13
14#[derive(Debug, Eq, PartialEq)]
15pub enum GeomPresTransformError {
16    ExceededScalingFactor { x: bool, y: bool },
17}
18
19impl fmt::Display for GeomPresTransformError {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        match self {
22            GeomPresTransformError::ExceededScalingFactor { x: true, y: false } => {
23                write!(f, "exceeded scaling factor on the X axis (-1.0 to 1.0)")
24            }
25            GeomPresTransformError::ExceededScalingFactor { x: false, y: true } => {
26                write!(f, "exceeded scaling factor on the Y axis (-1.0 to 1.0)")
27            }
28            GeomPresTransformError::ExceededScalingFactor { x: true, y: true } => {
29                write!(f, "exceeded scaling factor on both axis (-1.0 to 1.0)")
30            }
31            _ => panic!("cannot display invalid GeomPresTransformError"),
32        }
33    }
34}
35
36impl Error for GeomPresTransformError {}
37
38#[derive(Default, Clone, Copy, Debug, Eq, PartialEq)]
39pub struct GeomPresTransform(pub(crate) AffineTransform);
40
41impl GeomPresTransform {
42    /// ```text
43    /// [ x' ]   [ t.0 t.1 t.4 ] [ x ]
44    /// [ y' ] = [ t.2 t.3 t.5 ] [ y ]
45    /// [ 1  ]   [   0   0   1 ] [ 1 ]
46    /// ```
47    #[inline]
48    pub fn new(mut transform: [f32; 9]) -> Option<Self> {
49        (transform[6].abs() <= f32::EPSILON && transform[7].abs() <= f32::EPSILON)
50            .then(|| {
51                if (transform[8] - 1.0).abs() > f32::EPSILON {
52                    let recip = transform[8].recip();
53                    for val in &mut transform[..6] {
54                        *val *= recip;
55                    }
56                }
57
58                Self::try_from(AffineTransform {
59                    ux: transform[0],
60                    vx: transform[1],
61                    uy: transform[3],
62                    vy: transform[4],
63                    tx: transform[2],
64                    ty: transform[5],
65                })
66                .ok()
67            })
68            .flatten()
69    }
70
71    pub fn is_identity(&self) -> bool {
72        self.0.is_identity()
73    }
74
75    pub(crate) fn transform(&self, point: Point) -> Point {
76        self.0.transform(point)
77    }
78
79    #[inline]
80    pub fn as_slice(&self) -> [f32; 6] {
81        [self.0.ux, self.0.vx, self.0.uy, self.0.vy, self.0.tx, self.0.ty]
82    }
83}
84
85impl TryFrom<[f32; 6]> for GeomPresTransform {
86    type Error = GeomPresTransformError;
87    fn try_from(transform: [f32; 6]) -> Result<Self, Self::Error> {
88        GeomPresTransform::try_from(AffineTransform::from(transform))
89    }
90}
91
92impl TryFrom<AffineTransform> for GeomPresTransform {
93    type Error = GeomPresTransformError;
94
95    fn try_from(t: AffineTransform) -> Result<Self, Self::Error> {
96        let scales_up_x = t.ux * t.ux + t.uy * t.uy > MAX_SCALING_FACTOR_X;
97        let scales_up_y = t.vx * t.vx + t.vy * t.vy > MAX_SCALING_FACTOR_Y;
98
99        (!scales_up_x && !scales_up_y)
100            .then_some(Self(t))
101            .ok_or(GeomPresTransformError::ExceededScalingFactor { x: scales_up_x, y: scales_up_y })
102    }
103}
104
105#[cfg(test)]
106mod tests {
107    use super::*;
108
109    #[test]
110    fn default_identity() {
111        let transform = GeomPresTransform::default();
112
113        assert_eq!(transform.transform(Point::new(2.0, 3.0)), Point::new(2.0, 3.0));
114    }
115
116    #[test]
117    fn as_slice() {
118        let slice = [0.1, 0.5, 0.4, 0.3, 0.7, 0.9];
119
120        assert_eq!(slice, GeomPresTransform::try_from(slice).unwrap().as_slice());
121    }
122
123    #[test]
124    fn scale_translate() {
125        let transform = GeomPresTransform::try_from([0.1, 0.5, 0.4, 0.3, 0.5, 0.6]).unwrap();
126
127        assert_eq!(transform.transform(Point::new(2.0, 3.0)), Point::new(2.2, 2.3));
128    }
129
130    #[test]
131    fn wrong_scaling_factor() {
132        let transform =
133            [0.1, MAX_SCALING_FACTOR_Y.sqrt(), MAX_SCALING_FACTOR_X.sqrt(), 0.1, 0.5, 0.0];
134
135        assert_eq!(
136            GeomPresTransform::try_from(transform),
137            Err(GeomPresTransformError::ExceededScalingFactor { x: true, y: true })
138        );
139    }
140
141    #[test]
142    fn wrong_scaling_factor_x() {
143        let transform = [0.1, 0.0, MAX_SCALING_FACTOR_X.sqrt(), 0.0, 0.5, 0.0];
144
145        assert_eq!(
146            GeomPresTransform::try_from(transform),
147            Err(GeomPresTransformError::ExceededScalingFactor { x: true, y: false })
148        );
149    }
150
151    #[test]
152    fn wrong_scaling_factor_y() {
153        let transform = [0.0, MAX_SCALING_FACTOR_Y.sqrt(), 0.0, 0.1, 0.5, 0.0];
154
155        assert_eq!(
156            GeomPresTransform::try_from(transform),
157            Err(GeomPresTransformError::ExceededScalingFactor { x: false, y: true })
158        );
159    }
160
161    #[test]
162    fn correct_scaling_factor() {
163        let transform = [1.0, MAX_SCALING_FACTOR_Y.sqrt(), 0.0, 0.0, 0.5, 0.0];
164
165        assert_eq!(transform, GeomPresTransform::try_from(transform).unwrap().as_slice());
166    }
167}