screencapturekit/
cg.rs

1//! Core Graphics types for screen coordinates and dimensions
2//!
3//! This module provides Rust equivalents of Core Graphics types used in
4//! `ScreenCaptureKit` for representing screen coordinates, sizes, and rectangles.
5
6use std::fmt;
7
8/// `CGRect` representation
9///
10/// Represents a rectangle with origin (x, y) and dimensions (width, height).
11///
12/// # Examples
13///
14/// ```
15/// use screencapturekit::cg::CGRect;
16///
17/// let rect = CGRect::new(10.0, 20.0, 100.0, 200.0);
18/// assert_eq!(rect.x, 10.0);
19/// assert_eq!(rect.width, 100.0);
20/// assert_eq!(rect.max_x(), 110.0);
21/// ```
22#[repr(C)]
23#[derive(Debug, Clone, Copy, PartialEq)]
24pub struct CGRect {
25    pub x: f64,
26    pub y: f64,
27    pub width: f64,
28    pub height: f64,
29}
30
31impl std::hash::Hash for CGRect {
32    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
33        self.x.to_bits().hash(state);
34        self.y.to_bits().hash(state);
35        self.width.to_bits().hash(state);
36        self.height.to_bits().hash(state);
37    }
38}
39
40impl Eq for CGRect {}
41
42impl CGRect {
43    /// Create a new rectangle
44    ///
45    /// # Examples
46    ///
47    /// ```
48    /// use screencapturekit::cg::CGRect;
49    ///
50    /// let rect = CGRect::new(0.0, 0.0, 1920.0, 1080.0);
51    /// assert_eq!(rect.width, 1920.0);
52    /// ```
53    pub const fn new(x: f64, y: f64, width: f64, height: f64) -> Self {
54        Self {
55            x,
56            y,
57            width,
58            height,
59        }
60    }
61
62    /// Create a zero-sized rectangle at origin
63    ///
64    /// # Examples
65    ///
66    /// ```
67    /// use screencapturekit::cg::CGRect;
68    ///
69    /// let rect = CGRect::zero();
70    /// assert!(rect.is_null());
71    /// ```
72    pub const fn zero() -> Self {
73        Self::new(0.0, 0.0, 0.0, 0.0)
74    }
75
76    /// Create a rect with origin and size
77    pub const fn with_origin_and_size(origin: CGPoint, size: CGSize) -> Self {
78        Self {
79            x: origin.x,
80            y: origin.y,
81            width: size.width,
82            height: size.height,
83        }
84    }
85
86    /// Get the origin point
87    pub const fn origin(&self) -> CGPoint {
88        CGPoint::new(self.x, self.y)
89    }
90
91    /// Get the size
92    pub const fn size(&self) -> CGSize {
93        CGSize::new(self.width, self.height)
94    }
95
96    /// Get the center point
97    pub const fn center(&self) -> CGPoint {
98        CGPoint::new(self.x + self.width / 2.0, self.y + self.height / 2.0)
99    }
100
101    /// Get the minimum X coordinate
102    pub const fn min_x(&self) -> f64 {
103        self.x
104    }
105
106    /// Get the minimum Y coordinate
107    pub const fn min_y(&self) -> f64 {
108        self.y
109    }
110
111    /// Get the maximum X coordinate
112    pub const fn max_x(&self) -> f64 {
113        self.x + self.width
114    }
115
116    /// Get the maximum Y coordinate
117    pub const fn max_y(&self) -> f64 {
118        self.y + self.height
119    }
120
121    /// Get the mid X coordinate
122    pub const fn mid_x(&self) -> f64 {
123        self.x + self.width / 2.0
124    }
125
126    /// Get the mid Y coordinate
127    pub const fn mid_y(&self) -> f64 {
128        self.y + self.height / 2.0
129    }
130
131    pub fn is_empty(&self) -> bool {
132        self.width <= 0.0 || self.height <= 0.0
133    }
134
135    /// Check if rect is null (both position and size are zero)
136    pub const fn is_null(&self) -> bool {
137        self.x == 0.0 && self.y == 0.0 && self.width == 0.0 && self.height == 0.0
138    }
139}
140
141impl Default for CGRect {
142    fn default() -> Self {
143        Self::zero()
144    }
145}
146
147impl fmt::Display for CGRect {
148    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
149        write!(
150            f,
151            "({}, {}, {}, {})",
152            self.x, self.y, self.width, self.height
153        )
154    }
155}
156
157/// `CGSize` representation
158///
159/// Represents a 2D size with width and height.
160///
161/// # Examples
162///
163/// ```
164/// use screencapturekit::cg::CGSize;
165///
166/// let size = CGSize::new(1920.0, 1080.0);
167/// assert_eq!(size.aspect_ratio(), 1920.0 / 1080.0);
168/// assert_eq!(size.area(), 1920.0 * 1080.0);
169/// ```
170#[repr(C)]
171#[derive(Debug, Clone, Copy, PartialEq)]
172pub struct CGSize {
173    pub width: f64,
174    pub height: f64,
175}
176
177impl std::hash::Hash for CGSize {
178    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
179        self.width.to_bits().hash(state);
180        self.height.to_bits().hash(state);
181    }
182}
183
184impl Eq for CGSize {}
185
186impl CGSize {
187    /// Create a new size
188    ///
189    /// # Examples
190    ///
191    /// ```
192    /// use screencapturekit::cg::CGSize;
193    ///
194    /// let size = CGSize::new(800.0, 600.0);
195    /// assert_eq!(size.width, 800.0);
196    /// ```
197    pub const fn new(width: f64, height: f64) -> Self {
198        Self { width, height }
199    }
200
201    /// Create a zero-sized size
202    ///
203    /// # Examples
204    ///
205    /// ```
206    /// use screencapturekit::cg::CGSize;
207    ///
208    /// let size = CGSize::zero();
209    /// assert!(size.is_null());
210    /// ```
211    pub const fn zero() -> Self {
212        Self::new(0.0, 0.0)
213    }
214
215    /// Get the area (width * height)
216    pub const fn area(&self) -> f64 {
217        self.width * self.height
218    }
219
220    /// Get the aspect ratio (width / height)
221    pub fn aspect_ratio(&self) -> f64 {
222        if self.height == 0.0 {
223            0.0
224        } else {
225            self.width / self.height
226        }
227    }
228
229    /// Check if this is a square (width == height)
230    /// Note: Uses exact comparison, may not work well with computed values
231    #[allow(clippy::float_cmp)]
232    pub const fn is_square(&self) -> bool {
233        self.width == self.height
234    }
235
236    pub fn is_empty(&self) -> bool {
237        self.width <= 0.0 || self.height <= 0.0
238    }
239
240    /// Check if size is null (both dimensions are zero)
241    pub const fn is_null(&self) -> bool {
242        self.width == 0.0 && self.height == 0.0
243    }
244}
245
246impl Default for CGSize {
247    fn default() -> Self {
248        Self::zero()
249    }
250}
251
252impl fmt::Display for CGSize {
253    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254        write!(f, "{}x{}", self.width, self.height)
255    }
256}
257
258/// `CGPoint` representation
259///
260/// Represents a point in 2D space.
261///
262/// # Examples
263///
264/// ```
265/// use screencapturekit::cg::CGPoint;
266///
267/// let p1 = CGPoint::new(0.0, 0.0);
268/// let p2 = CGPoint::new(3.0, 4.0);
269/// assert_eq!(p1.distance_to(&p2), 5.0);
270/// ```
271#[repr(C)]
272#[derive(Debug, Clone, Copy, PartialEq)]
273pub struct CGPoint {
274    pub x: f64,
275    pub y: f64,
276}
277
278impl std::hash::Hash for CGPoint {
279    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
280        self.x.to_bits().hash(state);
281        self.y.to_bits().hash(state);
282    }
283}
284
285impl Eq for CGPoint {}
286
287impl CGPoint {
288    /// Create a new point
289    ///
290    /// # Examples
291    ///
292    /// ```
293    /// use screencapturekit::cg::CGPoint;
294    ///
295    /// let point = CGPoint::new(100.0, 200.0);
296    /// assert_eq!(point.x, 100.0);
297    /// ```
298    pub const fn new(x: f64, y: f64) -> Self {
299        Self { x, y }
300    }
301
302    /// Create a point at origin (0, 0)
303    ///
304    /// # Examples
305    ///
306    /// ```
307    /// use screencapturekit::cg::CGPoint;
308    ///
309    /// let point = CGPoint::zero();
310    /// assert!(point.is_zero());
311    /// ```
312    pub const fn zero() -> Self {
313        Self::new(0.0, 0.0)
314    }
315
316    /// Check if point is at origin (0, 0)
317    pub const fn is_zero(&self) -> bool {
318        self.x == 0.0 && self.y == 0.0
319    }
320
321    /// Calculate distance to another point
322    pub fn distance_to(&self, other: &Self) -> f64 {
323        let dx = self.x - other.x;
324        let dy = self.y - other.y;
325        dx.hypot(dy)
326    }
327
328    /// Calculate squared distance to another point (faster than `distance_to`)
329    pub const fn distance_squared_to(&self, other: &Self) -> f64 {
330        let dx = self.x - other.x;
331        let dy = self.y - other.y;
332        dx * dx + dy * dy
333    }
334}
335
336impl Default for CGPoint {
337    fn default() -> Self {
338        Self::zero()
339    }
340}
341
342impl fmt::Display for CGPoint {
343    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
344        write!(f, "({}, {})", self.x, self.y)
345    }
346}
347
348/// `CGDisplayID` type alias
349pub type CGDisplayID = u32;