Skip to main content

pax_runtime_api/
platform.rs

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