Skip to main content

apple_cf/cg/
affine.rs

1//! `CGAffineTransform` and `CGVector` value types.
2//!
3//! `CGAffineTransform` is the 2-D transformation matrix used throughout
4//! Core Graphics / Core Animation / Core Image:
5//!
6//! ```text
7//!     [ a   b   0 ]
8//!     [ c   d   0 ]
9//!     [ tx  ty  1 ]
10//! ```
11//!
12//! Wraps Apple's `CGAffineTransformMake*` / `CGAffineTransformConcat`
13//! helpers. Layout-compatible with the C struct so it crosses the FFI
14//! boundary by value.
15
16/// A 2-D vector — `(dx, dy)` in screen coordinates.
17#[derive(Debug, Clone, Copy, Default, PartialEq)]
18#[repr(C)]
19pub struct CGVector {
20    pub dx: f64,
21    pub dy: f64,
22}
23
24impl CGVector {
25    #[must_use]
26    pub const fn new(dx: f64, dy: f64) -> Self {
27        Self { dx, dy }
28    }
29}
30
31/// Affine transformation matrix.
32#[derive(Debug, Clone, Copy, PartialEq)]
33#[repr(C)]
34pub struct CGAffineTransform {
35    pub a: f64,
36    pub b: f64,
37    pub c: f64,
38    pub d: f64,
39    pub tx: f64,
40    pub ty: f64,
41}
42
43impl Default for CGAffineTransform {
44    fn default() -> Self {
45        Self::IDENTITY
46    }
47}
48
49impl CGAffineTransform {
50    /// The identity transform — no scale, rotation, or translation.
51    pub const IDENTITY: Self = Self {
52        a: 1.0,
53        b: 0.0,
54        c: 0.0,
55        d: 1.0,
56        tx: 0.0,
57        ty: 0.0,
58    };
59
60    /// Raw constructor; component values match Apple's
61    /// `CGAffineTransformMake(a, b, c, d, tx, ty)`.
62    #[must_use]
63    pub const fn new(a: f64, b: f64, c: f64, d: f64, tx: f64, ty: f64) -> Self {
64        Self { a, b, c, d, tx, ty }
65    }
66
67    /// Translation-only transform — wraps `CGAffineTransformMakeTranslation`.
68    #[must_use]
69    pub fn translation(tx: f64, ty: f64) -> Self {
70        unsafe { CGAffineTransformMakeTranslation(tx, ty) }
71    }
72
73    /// Scale-only transform — wraps `CGAffineTransformMakeScale`.
74    #[must_use]
75    pub fn scale(sx: f64, sy: f64) -> Self {
76        unsafe { CGAffineTransformMakeScale(sx, sy) }
77    }
78
79    /// Rotation-only transform (radians) — wraps `CGAffineTransformMakeRotation`.
80    #[must_use]
81    pub fn rotation(radians: f64) -> Self {
82        unsafe { CGAffineTransformMakeRotation(radians) }
83    }
84
85    /// `self` then `other` (Apple's matrix-multiply order). Wraps
86    /// `CGAffineTransformConcat`.
87    #[must_use]
88    pub fn concat(self, other: Self) -> Self {
89        unsafe { CGAffineTransformConcat(self, other) }
90    }
91
92    /// Inverse transform. Wraps `CGAffineTransformInvert`. Returns
93    /// the identity if `self` is non-invertible.
94    #[must_use]
95    pub fn invert(self) -> Self {
96        unsafe { CGAffineTransformInvert(self) }
97    }
98
99    /// True if this is the identity transform. Wraps
100    /// `CGAffineTransformIsIdentity`.
101    #[must_use]
102    pub fn is_identity(self) -> bool {
103        unsafe { CGAffineTransformIsIdentity(self) }
104    }
105}
106
107#[link(name = "ApplicationServices", kind = "framework")]
108extern "C" {
109    fn CGAffineTransformMakeTranslation(tx: f64, ty: f64) -> CGAffineTransform;
110    fn CGAffineTransformMakeScale(sx: f64, sy: f64) -> CGAffineTransform;
111    fn CGAffineTransformMakeRotation(radians: f64) -> CGAffineTransform;
112    fn CGAffineTransformConcat(
113        a: CGAffineTransform,
114        b: CGAffineTransform,
115    ) -> CGAffineTransform;
116    fn CGAffineTransformInvert(t: CGAffineTransform) -> CGAffineTransform;
117    fn CGAffineTransformIsIdentity(t: CGAffineTransform) -> bool;
118}