ttf_view/types/affine2x3.rs
1use crate::types::Fixed;
2
3/// A simple [`Fixed`] 2×3 affine transformation
4/// <span class="hidden">`[xx, yx; xy, yy; dx, dy]`</span>
5/// <math><mo>[</mo><mtable>
6/// <mtr><mtd><mi>xx</mi></mtd><mtd><mi>yx</mi></mtd></mtr>
7/// <mtr><mtd><mi>xy</mi></mtd><mtd><mi>yy</mi></mtd></mtr>
8/// <mtr><mtd><mi>dx</mi></mtd><mtd><mi>dy</mi></mtd></mtr>
9/// </mtable><mo>]</mo></math>.
10#[derive(Clone, Copy, PartialEq, Eq, Hash)]
11pub struct Affine2x3 {
12 /// X-component of transformed X-basis vector.
13 pub xx: Fixed,
14 /// Y-component of transformed X-basis vector.
15 pub yx: Fixed,
16 /// X-component of transformed Y-basis vector.
17 pub xy: Fixed,
18 /// Y-component of transformed Y-basis vector.
19 pub yy: Fixed,
20 /// Translation in X direction.
21 pub dx: Fixed,
22 /// Translation in Y direction.
23 pub dy: Fixed,
24}
25
26const impl Default for Affine2x3 {
27 fn default() -> Self {
28 Self::IDENTITY
29 }
30}
31
32impl Affine2x3 {
33 /// The identity transform
34 /// <span class="hidden">`[1, 0; 0, 1; 0, 0]`</span>
35 /// <math><mo>[</mo><mtable>
36 /// <mtr><mtd><mn>1</mn></mtd><mtd><mn>0</mn></mtd></mtr>
37 /// <mtr><mtd><mn>0</mn></mtd><mtd><mn>1</mn></mtd></mtr>
38 /// <mtr><mtd><mn>0</mn></mtd><mtd><mn>0</mn></mtd></mtr>
39 /// </mtable><mo>]</mo></math>.
40 ///
41 /// ```
42 /// # use ttf_view::types::Affine2x3;
43 /// assert_eq!(Affine2x3::IDENTITY.to_tuple_f64(), (1.0, 0.0, 0.0, 1.0, 0.0, 0.0));
44 /// ```
45 pub const IDENTITY: Self = Self::scale(Fixed::ONE);
46
47 /// Creates an [`Affine2x3`] with
48 /// <span class="hidden">`[xx, yx; xy, yy; dx, dy]`</span>
49 /// <math><mo>[</mo><mtable>
50 /// <mtr><mtd><mi>xx</mi></mtd><mtd><mi>yx</mi></mtd></mtr>
51 /// <mtr><mtd><mi>xy</mi></mtd><mtd><mi>yy</mi></mtd></mtr>
52 /// <mtr><mtd><mi>dx</mi></mtd><mtd><mi>dy</mi></mtd></mtr>
53 /// </mtable><mo>]</mo></math>.
54 pub const fn new(xx: Fixed, yx: Fixed, xy: Fixed, yy: Fixed, dx: Fixed, dy: Fixed) -> Self {
55 Self { xx, yx, xy, yy, dx, dy }
56 }
57 /// Creates an [`Affine2x3`] with
58 /// <span class="hidden">`[xx, yx; xy, yy; 0, 0]`</span>
59 /// <math><mo>[</mo><mtable>
60 /// <mtr><mtd><mi>xx</mi></mtd><mtd><mi>yx</mi></mtd></mtr>
61 /// <mtr><mtd><mi>xy</mi></mtd><mtd><mi>yy</mi></mtd></mtr>
62 /// <mtr><mtd><mn>0</mn></mtd><mtd><mn>0</mn></mtd></mtr>
63 /// </mtable><mo>]</mo></math>.
64 pub const fn transform(xx: Fixed, yx: Fixed, xy: Fixed, yy: Fixed) -> Self {
65 Self::new(xx, yx, xy, yy, Fixed::ZERO, Fixed::ZERO)
66 }
67 /// Creates an [`Affine2x3`] with
68 /// <span class="hidden">`[1, 0; 0, 1; dx, dy]`</span>
69 /// <math><mo>[</mo><mtable>
70 /// <mtr><mtd><mn>1</mn></mtd><mtd><mn>0</mn></mtd></mtr>
71 /// <mtr><mtd><mn>0</mn></mtd><mtd><mn>1</mn></mtd></mtr>
72 /// <mtr><mtd><mi>dx</mi></mtd><mtd><mi>dy</mi></mtd></mtr>
73 /// </mtable><mo>]</mo></math>.
74 pub const fn translate(dx: Fixed, dy: Fixed) -> Self {
75 Self::new(Fixed::ONE, Fixed::ZERO, Fixed::ZERO, Fixed::ONE, dx, dy)
76 }
77
78 /// Creates an [`Affine2x3`] with
79 /// <span class="hidden">`[scale, 0; 0, scale; 0, 0]`</span>
80 /// <math><mo>[</mo><mtable>
81 /// <mtr><mtd><mi>scale</mi></mtd><mtd><mn>0</mn></mtd></mtr>
82 /// <mtr><mtd><mn>0</mn></mtd><mtd><mi>scale</mi></mtd></mtr>
83 /// <mtr><mtd><mn>0</mn></mtd><mtd><mn>0</mn></mtd></mtr>
84 /// </mtable><mo>]</mo></math>.
85 pub const fn scale(scale: Fixed) -> Self {
86 Self::transform(scale, Fixed::ZERO, Fixed::ZERO, scale)
87 }
88 /// Creates an [`Affine2x3`] with
89 /// <span class="hidden">`[x, 0; 0, y; 0, 0]`</span>
90 /// <math><mo>[</mo><mtable>
91 /// <mtr><mtd><mi>x</mi></mtd><mtd><mn>0</mn></mtd></mtr>
92 /// <mtr><mtd><mn>0</mn></mtd><mtd><mi>y</mi></mtd></mtr>
93 /// <mtr><mtd><mn>0</mn></mtd><mtd><mn>0</mn></mtd></mtr>
94 /// </mtable><mo>]</mo></math>.
95 pub const fn scale_xy(x: Fixed, y: Fixed) -> Self {
96 Self::transform(x, Fixed::ZERO, Fixed::ZERO, y)
97 }
98
99 /// Creates an [`Affine2x3`] with
100 /// <span class="hidden">`[cos(θ),-sin(θ);sin(θ),cos(θ); 0, 0]`</span>
101 /// <math><mo>[</mo><mtable>
102 /// <mtr><mtd><mi>cos(θ)</mi></mtd><mtd><mi>-sin(θ)</mi></mtd></mtr>
103 /// <mtr><mtd><mi>sin(θ)</mi></mtd><mtd><mi>cos(θ)</mi></mtd></mtr>
104 /// <mtr><mtd><mn>0</mn></mtd><mtd><mn>0</mn></mtd></mtr>
105 /// </mtable><mo>]</mo></math>,
106 /// rotating the image counter-clockwise by specified angle.
107 pub fn rotation(radians: f64) -> Self {
108 let (sin, cos) = radians.sin_cos();
109 Self::transform(
110 Fixed::new(cos).unwrap(),
111 Fixed::new(-sin).unwrap(),
112 Fixed::new(sin).unwrap(),
113 Fixed::new(cos).unwrap(),
114 )
115 }
116 /// Creates an [`Affine2x3`] with
117 /// <span class="hidden">`[cos(θ),-sin(θ);sin(θ),cos(θ); 0, 0]`</span>
118 /// <math><mo>[</mo><mtable>
119 /// <mtr><mtd><mi>cos(θ)</mi></mtd><mtd><mi>-sin(θ)</mi></mtd></mtr>
120 /// <mtr><mtd><mi>sin(θ)</mi></mtd><mtd><mi>cos(θ)</mi></mtd></mtr>
121 /// <mtr><mtd><mn>0</mn></mtd><mtd><mn>0</mn></mtd></mtr>
122 /// </mtable><mo>]</mo></math>,
123 /// rotating the image counter-clockwise by specified angle.
124 pub fn rotation_degrees(degrees: f64) -> Self {
125 Self::rotation(degrees.to_radians())
126 }
127
128 /// Applies this [`Affine2x3`] transformation to the point
129 /// <span class="hidden">`[x, y, 1]`</span>
130 /// <math><mo>\[</mo><mi>x</mi><mo> </mo><mi>y</mi><mo> </mo><mn>1</mn><mo>\]</mo></math>:
131 ///
132 /// <code class="hidden">\[x,y,1\]×\[xx,yx; xy,yy; dx,dy\]=\[xx\*x+xy\*y+dx, yx\*x+yy\*y+dy\]</code>
133 /// <math>
134 /// <mrow><mo>\[</mo><mi>x</mi><mo> </mo><mi>y</mi><mo> </mo><mn>1</mn><mo>\]</mo></mrow>
135 /// <mo>×</mo>
136 /// <mo>\[</mo><mtable>
137 /// <mtr><mtd><mi>xx</mi></mtd><mtd><mi>yx</mi></mtd></mtr>
138 /// <mtr><mtd><mi>xy</mi></mtd><mtd><mi>yy</mi></mtd></mtr>
139 /// <mtr><mtd><mi>dx</mi></mtd><mtd><mi>dy</mi></mtd></mtr>
140 /// </mtable><mo>\]</mo>
141 /// <mo>=</mo>
142 /// <mrow><mo>\[</mo>
143 /// <mi>xx</mi><mo>\*</mo><mi>x</mi><mo>+</mo><mi>xy</mi><mo>\*</mo><mi>y</mi>
144 /// <mo>+</mo><mi>dx</mi>
145 /// <mo>, </mo>
146 /// <mi>yx</mi><mo>\*</mo><mi>x</mi><mo>+</mo><mi>yy</mi><mo>\*</mo><mi>y</mi>
147 /// <mo>+</mo><mi>dy</mi>
148 /// <mo>\]</mo></mrow>
149 /// </math>
150 ///
151 /// # Examples
152 ///
153 /// ```
154 /// use ttf_view::types::Affine2x3;
155 ///
156 /// assert_eq!(Affine2x3::IDENTITY.map_f64(10.0, -10.0), (10.0, -10.0));
157 /// assert_eq!(Affine2x3::IDENTITY.map_f64(-2583.2, 1842.2), (-2583.2, 1842.2));
158 ///
159 /// let rot90 = Affine2x3::rotation_degrees(90.0);
160 /// assert_eq!(rot90.map_f64(23.5, 9.8), (9.8, -23.5));
161 /// ```
162 pub const fn map(&self, x: Fixed, y: Fixed) -> (Fixed, Fixed) {
163 let new_x = Fixed::saturating_maddp(self.xx, x, self.xy, y, self.dx);
164 let new_y = Fixed::saturating_maddp(self.yx, x, self.yy, y, self.dy);
165 (new_x, new_y)
166 }
167
168 /// Applies this [`Affine2x3`] transformation to the point
169 /// <span class="hidden">`[x, y, 1]`</span>
170 /// <math><mo>\[</mo><mi>x</mi><mo> </mo><mi>y</mi><mo> </mo><mn>1</mn><mo>\]</mo></math>.
171 pub const fn map_f64(&self, x: f64, y: f64) -> (f64, f64) {
172 (self.xx * x + self.xy * y + self.dx.get(), self.yx * x + self.yy * y + self.dy.get())
173 }
174
175 // self × other
176 // [A B 0] [a b 0] [Aa+Bc Ab+Bd 0] [xx*xx+yx*xy xx*yx+yx*yy 0]
177 // [C D 0] × [c d 0] = [Ca+Dc Cb+Dd 0] = [xy*xx+yy*xy xy*yx+yy*yy 0]
178 // [X Y 1] [x y 1] [Xa+Yc+x Xb+Yd+y 1] [dx*xx+dy*xy+dx dx*yx+dy*yy+dy 1]
179
180 /// Multiplies this [`Affine2x3`] by another, wrapping and truncating at [`Fixed`]'s bounds.
181 pub const fn wrapping_mul(self, other: Self) -> Self {
182 Self::new(
183 Fixed::wrapping_maddp(self.xx, other.xx, self.yx, other.xy, Fixed::ZERO),
184 Fixed::wrapping_maddp(self.xx, other.yx, self.yx, other.yy, Fixed::ZERO),
185 Fixed::wrapping_maddp(self.xy, other.xx, self.yy, other.xy, Fixed::ZERO),
186 Fixed::wrapping_maddp(self.xy, other.yx, self.yy, other.yy, Fixed::ZERO),
187 Fixed::wrapping_maddp(self.dx, other.xx, self.dy, other.xy, self.dx),
188 Fixed::wrapping_maddp(self.dx, other.yx, self.dy, other.yy, self.dy),
189 )
190 }
191 /// Multiplies this [`Affine2x3`] by another, saturating at [`Fixed`]'s bounds.
192 pub const fn saturating_mul(self, other: Self) -> Self {
193 Self::new(
194 Fixed::saturating_maddp(self.xx, other.xx, self.yx, other.xy, Fixed::ZERO),
195 Fixed::saturating_maddp(self.xx, other.yx, self.yx, other.yy, Fixed::ZERO),
196 Fixed::saturating_maddp(self.xy, other.xx, self.yy, other.xy, Fixed::ZERO),
197 Fixed::saturating_maddp(self.xy, other.yx, self.yy, other.yy, Fixed::ZERO),
198 Fixed::saturating_maddp(self.dx, other.xx, self.dy, other.xy, self.dx),
199 Fixed::saturating_maddp(self.dx, other.yx, self.dy, other.yy, self.dy),
200 )
201 }
202 /// Multiplies this [`Affine2x3`] by another, returning `None` if overflow occurs.
203 pub const fn checked_mul(self, other: Self) -> Option<Self> {
204 Some(Self::new(
205 Fixed::checked_maddp(self.xx, other.xx, self.yx, other.xy, Fixed::ZERO)?,
206 Fixed::checked_maddp(self.xx, other.yx, self.yx, other.yy, Fixed::ZERO)?,
207 Fixed::checked_maddp(self.xy, other.xx, self.yy, other.xy, Fixed::ZERO)?,
208 Fixed::checked_maddp(self.xy, other.yx, self.yy, other.yy, Fixed::ZERO)?,
209 Fixed::checked_maddp(self.dx, other.xx, self.dy, other.xy, self.dx)?,
210 Fixed::checked_maddp(self.dx, other.yx, self.dy, other.yy, self.dy)?,
211 ))
212 }
213
214 /// Creates an [`Affine2x3`] from big-endian bytes.
215 pub const fn from_be_bytes(bytes: [u8; 24]) -> Self {
216 let (&[xx, yx, xy, yy, dx, dy], []) = bytes.as_chunks::<4>() else { panic!() };
217 Self::new(
218 Fixed::from_be_bytes(xx),
219 Fixed::from_be_bytes(yx),
220 Fixed::from_be_bytes(xy),
221 Fixed::from_be_bytes(yy),
222 Fixed::from_be_bytes(dx),
223 Fixed::from_be_bytes(dy),
224 )
225 }
226 /// Returns this [`Affine2x3`] as big-endian bytes.
227 pub const fn to_be_bytes(self) -> [u8; 24] {
228 let buf = [
229 self.xx.to_be_bytes(),
230 self.yx.to_be_bytes(),
231 self.xy.to_be_bytes(),
232 self.yy.to_be_bytes(),
233 self.dx.to_be_bytes(),
234 self.dy.to_be_bytes(),
235 ];
236 *buf.as_flattened().as_array().unwrap()
237 }
238
239 /// Returns this [`Affine2x3`]'s `(xx, yx, xy, yy, dx, dy)` as a tuple.
240 pub const fn to_tuple(self) -> (Fixed, Fixed, Fixed, Fixed, Fixed, Fixed) {
241 (self.xx, self.yx, self.xy, self.yy, self.dx, self.dy)
242 }
243 /// Returns this [`Affine2x3`]'s `(xx, yx, xy, yy, dx, dy)` as a tuple of [`f64`]s.
244 pub const fn to_tuple_f64(self) -> (f64, f64, f64, f64, f64, f64) {
245 (self.xx.get(), self.yx.get(), self.xy.get(), self.yy.get(), self.dx.get(), self.dy.get())
246 }
247
248 /// Creates an [`Affine2x3`] from a `[xx, yx, xy, yy, dx, dy]` array.
249 pub const fn from_array([xx, yx, xy, yy, dx, dy]: [Fixed; 6]) -> Self {
250 Self { xx, yx, xy, yy, dx, dy }
251 }
252 /// Returns this [`Affine2x3`]'s `[xx, yx, xy, yy, dx, dy]` as an array.
253 pub const fn to_array(self) -> [Fixed; 6] {
254 [self.xx, self.yx, self.xy, self.yy, self.dx, self.dy]
255 }
256}
257
258/// Performs multiplication `*` (panics on overflow in debug configuration).
259const impl std::ops::Mul for Affine2x3 {
260 type Output = Self;
261 fn mul(self, rhs: Self) -> Self::Output {
262 #[cfg(debug_assertions)]
263 return self.checked_mul(rhs).expect("attempt to multiply with overflow");
264 #[cfg(not(debug_assertions))]
265 return self.wrapping_mul(rhs);
266 }
267}
268
269impl std::fmt::Debug for Affine2x3 {
270 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
271 let Self { xx, yx, xy, yy, dx, dy } = *self;
272 write!(f, "[{:?}, {:?}; {:?}, {:?}; {:?}, {:?}]", xx, yx, xy, yy, dx, dy)
273 }
274}