Skip to main content

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}