Skip to main content

pax_runtime_api/
platform.rs

1//! Platform, viewport, and coordinate-space marker types.
2
3use super::*;
4
5impl Interpolatable for SafeAreaInsets {}
6
7/// Describes known operating systems / targets.
8#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
9pub enum OS {
10    /// macOS.
11    Mac,
12    /// Linux desktop.
13    Linux,
14    /// Windows desktop.
15    Windows,
16    /// Android.
17    Android,
18    /// iOS on iPhone-class devices.
19    IPhone,
20    /// iPadOS / iOS on iPad-class devices.
21    IPad,
22    /// OS has not been detected or reported.
23    #[default]
24    Unknown,
25}
26
27impl OS {
28    /// Helper to determine if the OS is a mobile platform.
29    pub fn is_mobile(&self) -> bool {
30        match self {
31            OS::Android | OS::IPhone | OS::IPad => true,
32            _ => false,
33        }
34    }
35
36    /// Helper to determine if the OS is a desktop platform.
37    pub fn is_desktop(&self) -> bool {
38        match self {
39            OS::Mac | OS::Linux | OS::Windows => true,
40            _ => false,
41        }
42    }
43
44    /// Returns true for either iPhone-class iOS or iPadOS.
45    pub fn is_ios(&self) -> bool {
46        matches!(self, OS::IPhone | OS::IPad)
47    }
48
49    /// Returns true for iPhone-class iOS.
50    pub fn is_iphone(&self) -> bool {
51        matches!(self, OS::IPhone)
52    }
53
54    /// Returns true for iPadOS / iPad-class iOS.
55    pub fn is_ipad(&self) -> bool {
56        matches!(self, OS::IPad)
57    }
58
59    /// Returns true for macOS.
60    pub fn is_macos(&self) -> bool {
61        matches!(self, OS::Mac)
62    }
63
64    /// Returns true for Android.
65    pub fn is_android(&self) -> bool {
66        matches!(self, OS::Android)
67    }
68
69    /// Returns true for Windows.
70    pub fn is_windows(&self) -> bool {
71        matches!(self, OS::Windows)
72    }
73
74    /// Returns true for Linux.
75    pub fn is_linux(&self) -> bool {
76        matches!(self, OS::Linux)
77    }
78}
79
80/// Describes categories of known platforms, for differentiating certain engine behaviors.
81#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
82pub enum Platform {
83    /// Browser-hosted rendering target.
84    Web,
85    /// Native application target.
86    Native,
87    /// Platform unknown or not yet reported.
88    #[default]
89    Unknown,
90}
91
92/// Runtime-inherited Apple liquid-glass effect scope.
93#[derive(Default, Debug, Clone, PartialEq)]
94pub struct NativeLiquidGlassScope {
95    /// Stable node id for the nearest liquid-glass scope.
96    pub group_id: u32,
97    /// Desired spacing between grouped glass surfaces, in pixels.
98    pub spacing: f64,
99    /// Whether supported Apple surfaces should use the interactive glass effect.
100    pub interactive: bool,
101    /// Optional tint for supported Apple surfaces.
102    pub tint: Option<Color>,
103    /// Apple glass style name, currently "regular" or "clear".
104    pub variant: String,
105}
106
107impl NativeLiquidGlassScope {
108    /// Converts runtime style data into the serialized native-message payload.
109    pub fn to_message(&self) -> AppleLiquidGlassPatch {
110        AppleLiquidGlassPatch {
111            group_id: self.group_id,
112            spacing: self.spacing,
113            interactive: self.interactive,
114            tint: self.tint.as_ref().map(Into::into),
115            variant: self.variant.clone(),
116        }
117    }
118}
119
120impl Interpolatable for NativeLiquidGlassScope {}
121
122impl Platform {
123    /// Returns true when hosted by the web chassis.
124    pub fn is_web(&self) -> bool {
125        matches!(self, Platform::Web)
126    }
127
128    /// Returns true when hosted by a native chassis.
129    pub fn is_native(&self) -> bool {
130        matches!(self, Platform::Native)
131    }
132}
133
134/// Derived target facts exposed to PAXEL and Rust event handlers.
135#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
136pub struct TargetInfo {
137    /// Browser-hosted rendering target.
138    pub web: bool,
139    /// Native application rendering target.
140    pub native: bool,
141    /// Any iOS-family target, including iPhone and iPad.
142    pub ios: bool,
143    /// iPhone-class iOS target.
144    pub iphone: bool,
145    /// iPadOS / iPad-class iOS target.
146    pub ipad: bool,
147    /// macOS target.
148    pub macos: bool,
149    /// Android target.
150    pub android: bool,
151    /// Windows target.
152    pub windows: bool,
153    /// Linux target.
154    pub linux: bool,
155    /// Any mobile OS target.
156    pub mobile: bool,
157    /// Any desktop OS target.
158    pub desktop: bool,
159}
160
161impl TargetInfo {
162    /// Build target facts from the chassis platform and detected OS.
163    pub fn new(platform: Platform, os: OS) -> Self {
164        Self {
165            web: platform.is_web(),
166            native: platform.is_native(),
167            ios: os.is_ios(),
168            iphone: os.is_iphone(),
169            ipad: os.is_ipad(),
170            macos: os.is_macos(),
171            android: os.is_android(),
172            windows: os.is_windows(),
173            linux: os.is_linux(),
174            mobile: os.is_mobile(),
175            desktop: os.is_desktop(),
176        }
177    }
178}
179
180impl ToPaxValue for TargetInfo {
181    fn to_pax_value(self) -> PaxValue {
182        PaxValue::Object(
183            vec![
184                ("web".to_string(), self.web.to_pax_value()),
185                ("native".to_string(), self.native.to_pax_value()),
186                ("ios".to_string(), self.ios.to_pax_value()),
187                ("iphone".to_string(), self.iphone.to_pax_value()),
188                ("ipad".to_string(), self.ipad.to_pax_value()),
189                ("macos".to_string(), self.macos.to_pax_value()),
190                ("android".to_string(), self.android.to_pax_value()),
191                ("windows".to_string(), self.windows.to_pax_value()),
192                ("linux".to_string(), self.linux.to_pax_value()),
193                ("mobile".to_string(), self.mobile.to_pax_value()),
194                ("desktop".to_string(), self.desktop.to_pax_value()),
195            ]
196            .into_iter()
197            .collect(),
198        )
199    }
200}
201
202impl Interpolatable for TargetInfo {}
203
204/// Struct representing the outermost viewport of a rendering scene, for example a browser window
205/// or native application window.
206#[derive(Default, Debug, Clone, Copy)]
207pub struct Viewport {
208    /// Viewport width in pixels.
209    pub width: f64,
210    /// Viewport height in pixels.
211    pub height: f64,
212    /// Larger viewport dimension in pixels.
213    pub major: f64,
214    /// Smaller viewport dimension in pixels.
215    pub minor: f64,
216    /// Width divided by height. Returns 0.0 when height is 0.
217    pub aspect: f64,
218    /// True when width is greater than height.
219    pub landscape: bool,
220    /// True when height is greater than width.
221    pub portrait: bool,
222    /// True when width and height are effectively equal.
223    pub square: bool,
224}
225
226impl Viewport {
227    /// Equality tolerance used when classifying square viewports.
228    pub const SQUARE_EPSILON: f64 = 0.5;
229
230    /// Build viewport facts from width and height in logical pixels.
231    pub fn new(width: f64, height: f64) -> Self {
232        let square = (width - height).abs() <= Self::SQUARE_EPSILON;
233        Self {
234            width,
235            height,
236            major: width.max(height),
237            minor: width.min(height),
238            aspect: if height.abs() <= f64::EPSILON {
239                0.0
240            } else {
241                width / height
242            },
243            landscape: width > height + Self::SQUARE_EPSILON,
244            portrait: height > width + Self::SQUARE_EPSILON,
245            square,
246        }
247    }
248}
249
250impl ToPaxValue for Viewport {
251    fn to_pax_value(self) -> PaxValue {
252        PaxValue::Object(
253            vec![
254                ("width".to_string(), self.width.to_pax_value()),
255                ("height".to_string(), self.height.to_pax_value()),
256                ("major".to_string(), self.major.to_pax_value()),
257                ("minor".to_string(), self.minor.to_pax_value()),
258                ("aspect".to_string(), self.aspect.to_pax_value()),
259                ("landscape".to_string(), self.landscape.to_pax_value()),
260                ("portrait".to_string(), self.portrait.to_pax_value()),
261                ("square".to_string(), self.square.to_pax_value()),
262            ]
263            .into_iter()
264            .collect(),
265        )
266    }
267}
268
269impl Interpolatable for Viewport {
270    fn interpolate(&self, other: &Self, t: f64) -> Self {
271        Viewport {
272            width: self.width + (other.width - self.width) * t,
273            height: self.height + (other.height - self.height) * t,
274            major: self.major + (other.major - self.major) * t,
275            minor: self.minor + (other.minor - self.minor) * t,
276            aspect: self.aspect + (other.aspect - self.aspect) * t,
277            landscape: self.landscape,
278            portrait: self.portrait,
279            square: self.square,
280        }
281    }
282}
283
284/// Current device orientation reported by a gyroscope/orientation sensor.
285///
286/// On web targets, this is sourced from `DeviceOrientationEvent` and mapped as:
287/// `x = beta`, `y = gamma`, and `z = alpha`, all in degrees.
288#[derive(Default, Debug, Clone, Copy, PartialEq)]
289pub struct Gyro {
290    /// Front-to-back tilt, in degrees.
291    pub x: f64,
292    /// Left-to-right tilt, in degrees.
293    pub y: f64,
294    /// Compass/z-axis rotation, in degrees.
295    pub z: f64,
296}
297
298impl ToPaxValue for Gyro {
299    fn to_pax_value(self) -> PaxValue {
300        PaxValue::Object(
301            vec![
302                ("x".to_string(), self.x.to_pax_value()),
303                ("y".to_string(), self.y.to_pax_value()),
304                ("z".to_string(), self.z.to_pax_value()),
305            ]
306            .into_iter()
307            .collect(),
308        )
309    }
310}
311
312impl Interpolatable for Gyro {
313    fn interpolate(&self, other: &Self, t: f64) -> Self {
314        Gyro {
315            x: self.x + (other.x - self.x) * t,
316            y: self.y + (other.y - self.y) * t,
317            z: self.z + (other.z - self.z) * t,
318        }
319    }
320}
321
322/// Current device acceleration, in meters per second squared.
323///
324/// On web targets, this uses `DeviceMotionEvent.accelerationIncludingGravity`
325/// when available, falling back to `DeviceMotionEvent.acceleration`.
326#[derive(Default, Debug, Clone, Copy, PartialEq)]
327pub struct Accel {
328    /// Acceleration along the x axis.
329    pub x: f64,
330    /// Acceleration along the y axis.
331    pub y: f64,
332    /// Acceleration along the z axis.
333    pub z: f64,
334}
335
336impl ToPaxValue for Accel {
337    fn to_pax_value(self) -> PaxValue {
338        PaxValue::Object(
339            vec![
340                ("x".to_string(), self.x.to_pax_value()),
341                ("y".to_string(), self.y.to_pax_value()),
342                ("z".to_string(), self.z.to_pax_value()),
343            ]
344            .into_iter()
345            .collect(),
346        )
347    }
348}
349
350impl Interpolatable for Accel {
351    fn interpolate(&self, other: &Self, t: f64) -> Self {
352        Accel {
353            x: self.x + (other.x - self.x) * t,
354            y: self.y + (other.y - self.y) * t,
355            z: self.z + (other.z - self.z) * t,
356        }
357    }
358}
359
360/// Phantom coordinate space representing the outer window.
361pub struct Window;
362
363impl Space for Window {}