1use std::{convert::TryFrom, error::Error, fmt, hash};
16
17use crate::{consts, math::Point, path::MAX_ERROR, utils::CanonBits};
18
19const MAX_SCALING_FACTOR_X: f32 = 1.0 + MAX_ERROR / consts::MAX_WIDTH as f32;
20const MAX_SCALING_FACTOR_Y: f32 = 1.0 + MAX_ERROR / consts::MAX_HEIGHT as f32;
21
22#[derive(Copy, Clone, Debug)]
33pub struct AffineTransform {
34 pub ux: f32,
35 pub uy: f32,
36 pub vx: f32,
37 pub vy: f32,
38 pub tx: f32,
39 pub ty: f32,
40}
41
42impl AffineTransform {
43 pub(crate) fn transform(&self, point: Point) -> Point {
44 Point {
45 x: self.ux.mul_add(point.x, self.vx.mul_add(point.y, self.tx)),
46 y: self.uy.mul_add(point.x, self.vy.mul_add(point.y, self.ty)),
47 }
48 }
49
50 pub(crate) fn is_identity(&self) -> bool {
51 *self == Self::default()
52 }
53
54 pub fn to_array(&self) -> [f32; 6] {
55 [self.ux, self.uy, self.vx, self.vy, self.tx, self.ty]
56 }
57}
58
59impl Eq for AffineTransform {}
60
61impl PartialEq for AffineTransform {
62 fn eq(&self, other: &Self) -> bool {
63 self.ux == other.ux
64 && self.uy == other.uy
65 && self.vx == other.vx
66 && self.vy == other.vy
67 && self.tx == other.tx
68 && self.ty == other.ty
69 }
70}
71
72impl hash::Hash for AffineTransform {
73 fn hash<H: hash::Hasher>(&self, state: &mut H) {
74 self.ux.to_canon_bits().hash(state);
75 self.uy.to_canon_bits().hash(state);
76 self.vx.to_canon_bits().hash(state);
77 self.vy.to_canon_bits().hash(state);
78 self.tx.to_canon_bits().hash(state);
79 self.ty.to_canon_bits().hash(state);
80 }
81}
82
83impl Default for AffineTransform {
84 fn default() -> Self {
85 Self {
86 ux: 1.0,
87 vx: 0.0,
88 tx: 0.0,
89 uy: 0.0,
90 vy: 1.0,
91 ty: 0.0,
92 }
93 }
94}
95
96impl From<[f32; 6]> for AffineTransform {
97 fn from(transform: [f32; 6]) -> Self {
98 Self {
99 ux: transform[0],
100 uy: transform[2],
101 vx: transform[1],
102 vy: transform[3],
103 tx: transform[4],
104 ty: transform[5],
105 }
106 }
107}
108
109#[derive(Debug, Eq, PartialEq)]
110pub enum GeomPresTransformError {
111 ExceededScalingFactor { x: bool, y: bool },
112}
113
114impl fmt::Display for GeomPresTransformError {
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 match self {
117 GeomPresTransformError::ExceededScalingFactor { x: true, y: false } => {
118 write!(f, "exceeded scaling factor on the X axis (-1.0 to 1.0)")
119 }
120 GeomPresTransformError::ExceededScalingFactor { x: false, y: true } => {
121 write!(f, "exceeded scaling factor on the Y axis (-1.0 to 1.0)")
122 }
123 GeomPresTransformError::ExceededScalingFactor { x: true, y: true } => {
124 write!(f, "exceeded scaling factor on both axis (-1.0 to 1.0)")
125 }
126 _ => panic!("cannot display invalid GeomPresTransformError"),
127 }
128 }
129}
130
131impl Error for GeomPresTransformError {}
132
133#[derive(Default, Clone, Copy, Debug, Eq, PartialEq)]
134pub struct GeomPresTransform(pub(crate) AffineTransform);
135
136impl GeomPresTransform {
137 #[inline]
143 pub fn new(mut transform: [f32; 9]) -> Option<Self> {
144 (transform[6].abs() <= f32::EPSILON && transform[7].abs() <= f32::EPSILON)
145 .then(|| {
146 if (transform[8] - 1.0).abs() > f32::EPSILON {
147 let recip = transform[8].recip();
148 for val in &mut transform[..6] {
149 *val *= recip;
150 }
151 }
152
153 Self::try_from(AffineTransform {
154 ux: transform[0],
155 vx: transform[1],
156 uy: transform[3],
157 vy: transform[4],
158 tx: transform[2],
159 ty: transform[5],
160 })
161 .ok()
162 })
163 .flatten()
164 }
165
166 #[inline]
167 pub fn is_identity(&self) -> bool {
168 self.0.is_identity()
169 }
170
171 pub(crate) fn transform(&self, point: Point) -> Point {
172 self.0.transform(point)
173 }
174
175 #[inline]
176 pub fn to_array(&self) -> [f32; 6] {
177 [
178 self.0.ux, self.0.vx, self.0.uy, self.0.vy, self.0.tx, self.0.ty,
179 ]
180 }
181}
182
183impl TryFrom<[f32; 6]> for GeomPresTransform {
184 type Error = GeomPresTransformError;
185 fn try_from(transform: [f32; 6]) -> Result<Self, Self::Error> {
186 GeomPresTransform::try_from(AffineTransform::from(transform))
187 }
188}
189
190impl TryFrom<AffineTransform> for GeomPresTransform {
191 type Error = GeomPresTransformError;
192
193 fn try_from(t: AffineTransform) -> Result<Self, Self::Error> {
194 let scales_up_x = t.ux * t.ux + t.uy * t.uy > MAX_SCALING_FACTOR_X;
195 let scales_up_y = t.vx * t.vx + t.vy * t.vy > MAX_SCALING_FACTOR_Y;
196
197 (!scales_up_x && !scales_up_y).then_some(Self(t)).ok_or(
198 GeomPresTransformError::ExceededScalingFactor {
199 x: scales_up_x,
200 y: scales_up_y,
201 },
202 )
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209
210 #[test]
211 fn default_identity() {
212 let transform = GeomPresTransform::default();
213
214 assert_eq!(
215 transform.transform(Point::new(2.0, 3.0)),
216 Point::new(2.0, 3.0)
217 );
218 }
219
220 #[test]
221 fn as_slice() {
222 let slice = [0.1, 0.5, 0.4, 0.3, 0.7, 0.9];
223
224 assert_eq!(
225 slice,
226 GeomPresTransform::try_from(slice).unwrap().to_array()
227 );
228 }
229
230 #[test]
231 fn scale_translate() {
232 let transform = GeomPresTransform::try_from([0.1, 0.5, 0.4, 0.3, 0.5, 0.6]).unwrap();
233
234 assert_eq!(
235 transform.transform(Point::new(2.0, 3.0)),
236 Point::new(2.2, 2.3)
237 );
238 }
239
240 #[test]
241 fn wrong_scaling_factor() {
242 let transform = [
243 0.1,
244 MAX_SCALING_FACTOR_Y.sqrt(),
245 MAX_SCALING_FACTOR_X.sqrt(),
246 0.1,
247 0.5,
248 0.0,
249 ];
250
251 assert_eq!(
252 GeomPresTransform::try_from(transform),
253 Err(GeomPresTransformError::ExceededScalingFactor { x: true, y: true })
254 );
255 }
256
257 #[test]
258 fn wrong_scaling_factor_x() {
259 let transform = [0.1, 0.0, MAX_SCALING_FACTOR_X.sqrt(), 0.0, 0.5, 0.0];
260
261 assert_eq!(
262 GeomPresTransform::try_from(transform),
263 Err(GeomPresTransformError::ExceededScalingFactor { x: true, y: false })
264 );
265 }
266
267 #[test]
268 fn wrong_scaling_factor_y() {
269 let transform = [0.0, MAX_SCALING_FACTOR_Y.sqrt(), 0.0, 0.1, 0.5, 0.0];
270
271 assert_eq!(
272 GeomPresTransform::try_from(transform),
273 Err(GeomPresTransformError::ExceededScalingFactor { x: false, y: true })
274 );
275 }
276
277 #[test]
278 fn correct_scaling_factor() {
279 let transform = [1.0, MAX_SCALING_FACTOR_Y.sqrt(), 0.0, 0.0, 0.5, 0.0];
280
281 assert_eq!(
282 transform,
283 GeomPresTransform::try_from(transform).unwrap().to_array()
284 );
285 }
286}