stereokit_rust/
util.rs

1use crate::{
2    StereoKitError,
3    maths::{Bool32T, Vec3, lerp},
4    sk::DisplayBlend,
5    system::TextContext,
6    ui::IdHashT,
7};
8use std::{
9    ffi::{CStr, CString, c_char, c_void},
10    fmt::Display,
11    ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign},
12    path::Path,
13    ptr::NonNull,
14};
15
16/// A color value stored as 4 floats with values that are generally between 0 and 1! Note that there’s also a Color32
17/// structure, and that 4 floats is generally a lot more than you need. So, use this for calculating individual
18/// colors at quality, but maybe store them en-masse with Color32!
19///
20/// Also note that RGB is often a terrible color format for picking colors, but it’s how our displays work and we’re
21/// stuck with it. If you want to create a color via code, try out the static Color.HSV method instead!
22///
23/// A note on gamma space vs. linear space colors! Color is not inherently one or the other, but most color values we
24/// work with tend to be gamma space colors, so most functions in StereoKit are gamma space. There are occasional
25/// functions that will ask for linear space colors instead, primarily in performance critical places, or places where
26/// a color may not always be a color! However, performing math on gamma space colors is bad, and will result in
27/// incorrect colors. We do our best to indicate what color space a function uses, but it’s not enforced through syntax!
28/// <https://stereokit.net/Pages/StereoKit/Color.html>
29///
30/// see also [`Color32`] [`named_colors`]
31/// ### Examples
32/// ```
33/// use stereokit_rust::{util::{Color128, named_colors}, maths::Vec3};
34///
35/// // Cyan from different sources:
36/// let color_cyan1: Color128  = named_colors::CYAN.into();
37/// let color_cyan2: Color128  = [0.0, 1.0, 1.0, 1.0].into();
38/// let color_cyan3 = Color128 {r: 0.0, g: 1.0, b: 1.0, a: 1.0};
39/// let color_cyan4 = Color128::new (0.0, 1.0, 1.0, 1.0);
40/// let color_cyan5 = Color128::rgba(0.0, 1.0, 1.0, 1.0);
41/// let color_cyan6 = Color128::rgb (0.0, 1.0, 1.0 );
42/// let color_cyan7 = Color128::hsv (0.5, 1.0, 1.0, 1.0);
43/// let color_cyan8 = Color128::hsv_vec3 (Vec3::new(0.5, 1.0, 1.0), 1.0);
44/// let color_cyan9 = Color128::lab (0.911, -0.493, 0.042, 1.0);
45/// let color_cyanA = Color128::lab_vec3 (Vec3::new(0.911, -0.493, 0.042), 1.0);
46/// let color_cyanB = Color128::hex (0x00FFFFFF);
47///
48/// # assert_eq!(color_cyan1, color_cyan2);
49/// # assert_eq!(color_cyan1, color_cyan3);
50/// # assert_eq!(color_cyan1, color_cyan4);
51/// # assert_eq!(color_cyan1, color_cyan5);
52/// # assert_eq!(color_cyan1, color_cyan6);
53/// # assert_eq!(color_cyan1, color_cyan7);
54/// # assert_eq!(color_cyan1, color_cyan8);
55/// # assert_eq!(color_cyan1, color_cyan9);
56/// # assert_eq!(color_cyan1, color_cyanA);
57/// assert_eq!(color_cyan1, color_cyanB);
58/// ```
59#[repr(C)]
60#[derive(Debug, Copy, Clone, Default)]
61pub struct Color128 {
62    pub r: f32,
63    pub g: f32,
64    pub b: f32,
65    pub a: f32,
66}
67
68impl PartialEq for Color128 {
69    fn eq(&self, other: &Self) -> bool {
70        (self.r - other.r).abs() < 0.000001
71            && (self.g - other.g).abs() < 0.000001
72            && (self.b - other.b).abs() < 0.000001
73            && (self.a - other.a).abs() < 0.000001
74    }
75}
76
77impl From<[f32; 4]> for Color128 {
78    fn from(s: [f32; 4]) -> Self {
79        Self::new(s[0], s[1], s[2], s[3])
80    }
81}
82
83impl From<Color32> for Color128 {
84    fn from(a: Color32) -> Self {
85        Self::new(a.r as f32 / 255.0, a.g as f32 / 255.0, a.b as f32 / 255.0, a.a as f32 / 255.0)
86    }
87}
88
89unsafe extern "C" {
90    pub fn color_hsv(hue: f32, saturation: f32, value: f32, transparency: f32) -> Color128;
91    pub fn color_to_hsv(color: *const Color128) -> Vec3;
92    pub fn color_lab(l: f32, a: f32, b: f32, transparency: f32) -> Color128;
93    pub fn color_to_lab(color: *const Color128) -> Vec3;
94    pub fn color_to_linear(srgb_gamma_correct: Color128) -> Color128;
95    pub fn color_to_gamma(srgb_linear: Color128) -> Color128;
96}
97
98impl Color128 {
99    /// Opaque black color
100    pub const BLACK: Color128 = Color128 { r: 0.0, g: 0.0, b: 0.0, a: 1.0 };
101
102    /// Transparent black color
103    pub const BLACK_TRANSPARENT: Color128 = Color128 { r: 0.0, g: 0.0, b: 0.0, a: 0.0 };
104
105    /// Opaque white color
106    pub const WHITE: Color128 = Color128 { r: 1.0, g: 1.0, b: 1.0, a: 1.0 };
107
108    /// Try hsv instead! But if you really need to create a color from RGB values, I suppose you’re in the right
109    /// place. All parameter values are generally in the range of 0-1.
110    /// <https://stereokit.net/Pages/StereoKit/Color/Color.html>
111    pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
112        Self { r, g, b, a }
113    }
114
115    /// Try hsv instead! But if you really need to create a color from RGB values, I suppose you’re in the right
116    /// place. All parameter values are generally in the range of 0-1.
117    /// <https://stereokit.net/Pages/StereoKit/Color/Color.html>
118    pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self {
119        Self { r, g, b, a }
120    }
121
122    /// Try hsv instead! But if you really need to create a color from RGB values, I suppose you’re in the right
123    /// place. All parameter values are generally in the range of 0-1. Alpha will be set to 1.0
124    /// <https://stereokit.net/Pages/StereoKit/Color/Color.html>
125    #[deprecated = "Use Color128::rgb instead"]
126    pub const fn new_rgb(r: f32, g: f32, b: f32) -> Self {
127        Self::new(r, g, b, 1.0)
128    }
129
130    /// Try hsv instead! But if you really need to create a color from RGB values, I suppose you’re in the right
131    /// place. All parameter values are generally in the range of 0-1. Alpha will be set to 1.0
132    /// <https://stereokit.net/Pages/StereoKit/Color/Color.html>
133    pub const fn rgb(r: f32, g: f32, b: f32) -> Self {
134        Self::new(r, g, b, 1.0)
135    }
136
137    /// Creates a Red/Green/Blue gamma space color from Hue/Saturation/Value information.
138    /// <https://stereokit.net/Pages/StereoKit/Color/HSV.html>
139    pub fn hsv(hue: f32, saturation: f32, value: f32, transparency: f32) -> Color128 {
140        unsafe { color_hsv(hue, saturation, value, transparency) }
141    }
142
143    /// Creates a Red/Green/Blue gamma space color from Hue/Saturation/Value information.
144    /// <https://stereokit.net/Pages/StereoKit/Color/HSV.html>
145    ///
146    /// see also [`color_hsv`]
147    pub fn hsv_vec3(vec: impl Into<Vec3>, transparency: f32) -> Self {
148        let vec = vec.into();
149        unsafe { color_hsv(vec.x, vec.y, vec.z, transparency) }
150    }
151
152    /// Creates a gamma space RGB color from a CIE-Lab color space. CIE-Lab is a color space that models human
153    /// perception, and has significantly more accurate to perception lightness values, so this is an excellent color
154    /// space for color operations that wish to preserve color brightness properly.
155    ///
156    /// Traditionally, values are L `[0,100], a,b [-200,+200]` but here we normalize them all to the 0-1 range. If you
157    /// hate it, let me know why!
158    /// <https://stereokit.net/Pages/StereoKit/Color/LAB.html>    
159    ///
160    /// see also [`color_lab`]
161    pub fn lab(l: f32, a: f32, b: f32, transparency: f32) -> Self {
162        unsafe { color_lab(l, a, b, transparency) }
163    }
164
165    /// Creates a gamma space RGB color from a CIE-Lab color space. CIE-Lab is a color space that models human
166    /// perception, and has significantly more accurate to perception lightness values, so this is an excellent color
167    /// space for color operations that wish to preserve color brightness properly.
168    ///
169    /// Traditionally, values are L `[0,100], a,b [-200,+200]` but here we normalize them all to the 0-1 range. If you
170    /// hate it, let me know why!
171    /// <https://stereokit.net/Pages/StereoKit/Color/LAB.html>    
172    ///
173    /// see also [`color_lab`]
174    pub fn lab_vec3(vec: Vec3, transparency: f32) -> Self {
175        unsafe { color_lab(vec.x, vec.y, vec.z, transparency) }
176    }
177
178    /// Create a color from an integer based hex value! This can make it easier to copy over colors from the web. This
179    /// isn’t a string function though, so you’ll need to fill it out the whole way. Ex: Color32::Hex(0x0000FFFF) would
180    /// be RGBA(0,0,1,1).
181    /// <https://stereokit.net/Pages/StereoKit/Color/Hex.html>    
182    pub fn hex(hex_value: u32) -> Self {
183        Self {
184            r: (hex_value >> 24) as f32 / 255.0,
185            g: ((hex_value >> 16) & 0x000000FF) as f32 / 255.0,
186            b: ((hex_value >> 8) & 0x000000FF) as f32 / 255.0,
187            a: (hex_value & 0x000000FF) as f32 / 255.0,
188        }
189    }
190
191    /// This will linearly blend between two different colors! Best done on linear colors, rather than gamma corrected
192    /// colors, but will work either way. This will not clamp the percentage to the 0-1 range.
193    /// <https://stereokit.net/Pages/StereoKit/Color/Lerp.html>    
194    /// * `a` - The first color, this will be the result if t is 0.
195    /// * `b` - The second color, this will be the result if t is 1.
196    /// * `t` - A percentage representing the blend between a and b. This is not clamped to the 0-1 range, and will
197    ///   result in extrapolation outside this range.
198    ///
199    /// see also [`crate::maths::lerp`]
200    /// ### Examples
201    /// ```
202    /// use stereokit_rust::{util::{Color128, named_colors}};
203    ///
204    /// let color_red: Color128  = named_colors::RED.into();
205    /// let color_blue: Color128  = named_colors::BLUE.into();
206    /// let color_mix = Color128::lerp(color_red, color_blue, 0.25);
207    /// assert_eq!(color_mix, Color128::rgb(0.75, 0.0, 0.25));
208    /// ```
209    pub fn lerp(a: Color128, b: Color128, t: f32) -> Self {
210        Self { r: lerp(a.r, b.r, t), g: lerp(a.g, b.g, t), b: lerp(a.b, b.b, t), a: lerp(a.a, b.a, t) }
211    }
212
213    /// Converts this from a gamma space color, into a linear space color! If this is not a gamma space color, this
214    /// will just make your color wacky!
215    /// <https://stereokit.net/Pages/StereoKit/Color/ToLinear.html>   
216    ///
217    /// see also [`color_to_linear`]
218    /// ### Examples
219    /// ```
220    /// use stereokit_rust::{util::{Color128, named_colors}};
221    ///
222    /// let color = Color128::rgb(0.75, 0.0, 0.25);
223    /// let color_linear = color.to_linear();
224    /// assert_eq!(color_linear, Color128 { r: 0.5310492, g: 0.0, b: 0.04736614, a: 1.0 });
225    /// ```
226    pub fn to_linear(&self) -> Self {
227        unsafe { color_to_linear(*self) }
228    }
229
230    /// Converts this from a linear space color, into a gamma space color! If this is not a linear space color, this
231    /// will just make your color wacky!
232    /// <https://stereokit.net/Pages/StereoKit/Color/ToGamma.html>
233    ///    
234    /// see also [`color_to_gamma`]
235    /// ### Examples
236    /// ```
237    /// use stereokit_rust::{util::{Color128, named_colors}};
238    ///
239    /// let color = Color128 { r: 0.5310492, g: 0.0, b: 0.04736614, a: 1.0 };
240    /// let color_gamma = color.to_gamma();
241    /// assert_eq!(color_gamma, Color128::rgb(0.75, 0.0, 0.25));
242    /// ```
243    pub fn to_gamma(&self) -> Self {
244        unsafe { color_to_gamma(*self) }
245    }
246
247    /// Converts the gamma space color to a Hue/Saturation/Value format! Does not consider transparency when
248    /// calculating the result.
249    /// <https://stereokit.net/Pages/StereoKit/Color/ToHSV.html>   
250    ///
251    /// Returns a Vec3 containing Hue, Saturation, and Value, stored in x, y, and z respectively. All values are
252    /// between 0-1.
253    /// see also [`color_to_hsv`]
254    /// ### Examples
255    /// ```
256    /// use stereokit_rust::{util::{Color128, named_colors}, maths::Vec3};
257    ///
258    /// let color = Color128::rgb(0.75, 0.0, 0.25);
259    /// let color_hsv = color.to_hsv();
260    /// assert_eq!(color_hsv, Vec3::new(0.9444444, 1.0, 0.75));
261    /// ```
262    pub fn to_hsv(&self) -> Vec3 {
263        unsafe { color_to_hsv(self) }
264    }
265
266    /// Converts the gamma space RGB color to a CIE LAB color space value! Conversion back and forth from LAB space
267    /// could be somewhat lossy.
268    /// <https://stereokit.net/Pages/StereoKit/Color/ToLAB.html>
269    ///
270    /// Returns a LAB Vec3 where x=L, y=A, z=B.    
271    /// see also [`color_to_lab`]
272    /// ### Examples
273    /// ```
274    /// use stereokit_rust::{util::{Color128, named_colors}, maths::Vec3};
275    ///
276    /// let color = Color128::rgb(0.75, 0.0, 0.25);
277    /// let color_lab = color.to_lab();
278    /// assert_eq!(color_lab, Vec3{ x: 0.403711, y: 0.6654343, z: 0.55437124 });
279    /// ```
280    pub fn to_lab(&self) -> Vec3 {
281        unsafe { color_to_lab(self) }
282    }
283}
284
285impl Display for Color128 {
286    /// Mostly for debug purposes, this is a decent way to log or inspect the color in debug mode. Looks like
287    /// “Color128 {r: 1.0, g: 1.0, b: 1.0, a: 1.0}”
288    /// <https://stereokit.net/Pages/StereoKit/Color/ToString.html>
289    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
290        write!(f, "r:{}, g:{}, b:{}, a:{}", self.r, self.g, self.b, self.a)
291    }
292}
293
294/// This will add a color component-wise with another color, including alpha. Best done on colors in linear space.
295/// No clamping is applied.
296/// <https://stereokit.net/Pages/StereoKit/Color/op_Addition.html>
297impl Add for Color128 {
298    type Output = Self;
299
300    #[inline]
301    fn add(self, rhs: Self) -> Self {
302        Color128 { r: self.r + rhs.r, g: self.g + rhs.g, b: self.b + rhs.b, a: self.a + rhs.a }
303    }
304}
305
306/// This will add a color component-wise with another color, including alpha. Best done on colors in linear space.
307/// No clamping is applied.
308/// <https://stereokit.net/Pages/StereoKit/Color/op_Addition.html>
309impl AddAssign for Color128 {
310    #[inline]
311    fn add_assign(&mut self, rhs: Self) {
312        self.r += rhs.r;
313        self.g += rhs.g;
314        self.b += rhs.b;
315        self.a += rhs.a;
316    }
317}
318
319/// This will subtract color b component-wise from color a, including alpha. Best done on colors in linear space. No
320/// clamping is applied.
321/// <https://stereokit.net/Pages/StereoKit/Color/op_Subtraction.html>
322impl Sub for Color128 {
323    type Output = Self;
324
325    #[inline]
326    fn sub(self, rhs: Self) -> Self {
327        Color128 { r: self.r - rhs.r, g: self.g - rhs.g, b: self.b - rhs.b, a: self.a - rhs.a }
328    }
329}
330
331/// This will subtract a color component-wise from another color, including alpha. Best done on colors in linear space.
332/// No clamping is applied.
333/// <https://stereokit.net/Pages/StereoKit/Color/op_Subtraction.html>
334impl SubAssign for Color128 {
335    #[inline]
336    fn sub_assign(&mut self, rhs: Self) {
337        self.r -= rhs.r;
338        self.g -= rhs.g;
339        self.b -= rhs.b;
340        self.a -= rhs.a;
341    }
342}
343
344/// This will divide a color component-wise against another color, including alpha. Best done on colors in linear space.
345/// No clamping is applied.
346/// <https://stereokit.net/Pages/StereoKit/Color/op_Division.html>
347impl Div<Color128> for Color128 {
348    type Output = Self;
349    #[inline]
350    fn div(self, rhs: Self) -> Self::Output {
351        Self { r: self.r.div(rhs.r), g: self.g.div(rhs.g), b: self.b.div(rhs.b), a: self.a.div(rhs.a) }
352    }
353}
354
355/// This will divide a color component-wise against another color, including alpha. Best done on colors in linear space.
356/// No clamping is applied.
357/// <https://stereokit.net/Pages/StereoKit/Color/op_Division.html>
358impl DivAssign<Color128> for Color128 {
359    #[inline]
360    fn div_assign(&mut self, rhs: Self) {
361        self.r.div_assign(rhs.r);
362        self.g.div_assign(rhs.g);
363        self.b.div_assign(rhs.b);
364        self.a.div_assign(rhs.a);
365    }
366}
367
368/// This will divide a color linearly, including alpha. Best done on a color in linear space. No clamping is applied.
369/// <https://stereokit.net/Pages/StereoKit/Color/op_Division.html>
370impl Div<f32> for Color128 {
371    type Output = Self;
372    #[inline]
373    fn div(self, rhs: f32) -> Self::Output {
374        Self { r: self.r.div(rhs), g: self.g.div(rhs), b: self.b.div(rhs), a: self.a.div(rhs) }
375    }
376}
377
378/// This will divide a color linearly, including alpha. Best done on a color in linear space. No clamping is applied.
379/// <https://stereokit.net/Pages/StereoKit/Color/op_Division.html>
380impl DivAssign<f32> for Color128 {
381    #[inline]
382    fn div_assign(&mut self, rhs: f32) {
383        self.r.div_assign(rhs);
384        self.g.div_assign(rhs);
385        self.b.div_assign(rhs);
386        self.a.div_assign(rhs);
387    }
388}
389
390/// This will divide a color linearly, including alpha. Best done on a color in linear space. No clamping is applied.
391/// <https://stereokit.net/Pages/StereoKit/Color/op_Division.html>
392impl Div<Color128> for f32 {
393    type Output = Color128;
394    #[inline]
395    fn div(self, rhs: Color128) -> Self::Output {
396        Self::Output { r: self.div(rhs.r), g: self.div(rhs.g), b: self.div(rhs.b), a: self.div(rhs.a) }
397    }
398}
399
400/// This will multiply a color component-wise against another color, including alpha. Best done on colors in linear
401/// space. No clamping is applied.
402/// <https://stereokit.net/Pages/StereoKit/Color/op_Multiply.html>
403impl Mul<Color128> for Color128 {
404    type Output = Self;
405    #[inline]
406    fn mul(self, rhs: Self) -> Self::Output {
407        Self { r: self.r.mul(rhs.r), g: self.g.mul(rhs.g), b: self.b.mul(rhs.b), a: self.a.mul(rhs.a) }
408    }
409}
410
411/// This will multiply a color component-wise against another color, including alpha. Best done on colors in linear
412/// space. No clamping is applied.
413/// <https://stereokit.net/Pages/StereoKit/Color/op_Multiply.html>
414impl MulAssign<Color128> for Color128 {
415    #[inline]
416    fn mul_assign(&mut self, rhs: Self) {
417        self.r.mul_assign(rhs.r);
418        self.g.mul_assign(rhs.g);
419        self.b.mul_assign(rhs.b);
420        self.a.mul_assign(rhs.a);
421    }
422}
423
424/// This will multiply a color linearly, including alpha. Best done on a color in linear space. No clamping is applied.
425/// <https://stereokit.net/Pages/StereoKit/Color/op_Multiply.html>
426impl Mul<f32> for Color128 {
427    type Output = Self;
428    #[inline]
429    fn mul(self, rhs: f32) -> Self::Output {
430        Self { r: self.r.mul(rhs), g: self.g.mul(rhs), b: self.b.mul(rhs), a: self.a.mul(rhs) }
431    }
432}
433
434/// This will multiply a color linearly, including alpha. Best done on a color in linear space. No clamping is applied.
435/// <https://stereokit.net/Pages/StereoKit/Color/op_Multiply.html>
436impl MulAssign<f32> for Color128 {
437    #[inline]
438    fn mul_assign(&mut self, rhs: f32) {
439        self.r.mul_assign(rhs);
440        self.g.mul_assign(rhs);
441        self.b.mul_assign(rhs);
442        self.a.mul_assign(rhs);
443    }
444}
445
446/// This will multiply a color linearly, including alpha. Best done on a color in linear space. No clamping is applied.
447/// <https://stereokit.net/Pages/StereoKit/Color/op_Multiply.html>
448impl Mul<Color128> for f32 {
449    type Output = Color128;
450    #[inline]
451    fn mul(self, rhs: Color128) -> Self::Output {
452        Self::Output { r: self.mul(rhs.r), g: self.mul(rhs.g), b: self.mul(rhs.b), a: self.mul(rhs.a) }
453    }
454}
455
456/// A 32 bit color struct! This is often directly used by StereoKit data structures, and so is often necessary for
457/// setting texture data, or mesh data. Note that the Color type implicitly converts to Color32, so you can use the
458/// static methods there to create Color32 values!
459///
460/// It’s generally best to avoid doing math on 32-bit color values, as they lack the precision necessary to create
461/// results. It’s best to think of a Color32 as an optimized end stage format of a color.
462/// <https://stereokit.net/Pages/StereoKit/Color32.html>
463///
464/// see also [`Color128`]
465/// ### Examples
466/// ```
467/// use stereokit_rust::util::{Color32, named_colors, Color128};
468///
469/// // Cyan from different sources:
470/// let color_cyan1 = named_colors::CYAN;
471/// let color_cyan2: Color32  = [0, 255, 255, 255].into();
472/// let color_cyan3: Color32 = Color128 { r: 0.0, g: 1.0, b: 1.0, a: 1.0 }.into();
473/// let color_cyan4 = Color32 {r: 0, g: 255, b: 255, a: 255};
474/// let color_cyan5 = Color32::new (0, 255, 255, 255);
475/// let color_cyan6 = Color32::rgba(0, 255, 255, 255);
476/// let color_cyan7 = Color32::rgb (0, 255, 255 );
477/// let color_cyan8 = Color32::hex (0x00FFFFFF);
478///
479/// # assert_eq!(color_cyan1, color_cyan2);
480/// # assert_eq!(color_cyan1, color_cyan3);
481/// # assert_eq!(color_cyan1, color_cyan4);
482/// # assert_eq!(color_cyan1, color_cyan5);
483/// # assert_eq!(color_cyan1, color_cyan6);
484/// # assert_eq!(color_cyan1, color_cyan7);
485/// assert_eq!(color_cyan1, color_cyan8);
486/// ```
487#[repr(C)]
488#[derive(Debug, Copy, Clone, Default, PartialEq)]
489pub struct Color32 {
490    pub r: u8,
491    pub g: u8,
492    pub b: u8,
493    pub a: u8,
494}
495
496impl From<Color128> for Color32 {
497    fn from(a: Color128) -> Self {
498        Self::new((a.r * 255.0) as u8, (a.g * 255.0) as u8, (a.b * 255.0) as u8, (a.a * 255.0) as u8)
499    }
500}
501impl From<[u8; 4]> for Color32 {
502    fn from(s: [u8; 4]) -> Self {
503        Self::new(s[0], s[1], s[2], s[3])
504    }
505}
506
507impl Color32 {
508    /// Opaque black color.
509    pub const BLACK: Color32 = Color32 { r: 0, g: 0, b: 0, a: 255 };
510
511    /// transparent black color.
512    pub const BLACK_TRANSPARENT: Color32 = Color32 { r: 0, g: 0, b: 0, a: 0 };
513
514    /// Opaque white color.
515    pub const WHITE: Color32 = Color32 { r: 255, g: 255, b: 255, a: 255 };
516
517    /// Constructs a 32-bit color from bytes! You may also be interested in Color32::hex.
518    /// <https://stereokit.net/Pages/StereoKit/Color32/Color32.html>
519    pub const fn new(r: u8, g: u8, b: u8, a: u8) -> Self {
520        Self { r, g, b, a }
521    }
522
523    /// Constructs a 32-bit color from bytes! You may also be interested in Color32::hex.
524    /// <https://stereokit.net/Pages/StereoKit/Color32/Color32.html>
525    pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
526        Self { r, g, b, a }
527    }
528
529    /// Constructs a 32-bit color from bytes! You may also be interested in Color32::hex.
530    /// <https://stereokit.net/Pages/StereoKit/Color32/Color32.html>
531    pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
532        Self { r, g, b, a: 255 }
533    }
534
535    /// Constructs a 32-bit color from bytes! You may also be interested in Color32::hex.
536    /// a is set to 255 !
537    /// <https://stereokit.net/Pages/StereoKit/Color32/Color32.html>
538    #[deprecated = "Use Color32::rgb instead"]
539    pub const fn new_rgb(r: u8, g: u8, b: u8) -> Self {
540        Self { r, g, b, a: 255 }
541    }
542
543    /// Create a color from an integer based hex value! This can make it easier to copy over colors from the web. This
544    /// isn’t a string function though, so you’ll need to fill it out the whole way. Ex: Color.Hex(0x0000FFFF) would be
545    /// RGBA(0,0,255,255).
546    /// <https://stereokit.net/Pages/StereoKit/Color32/Hex.html>
547    pub fn hex(hex_value: u32) -> Self {
548        Self {
549            r: (hex_value >> 24) as u8,
550            g: ((hex_value >> 16) & 0x000000FF) as u8,
551            b: ((hex_value >> 8) & 0x000000FF) as u8,
552            a: (hex_value & 0x000000FF) as u8,
553        }
554    }
555}
556
557impl Display for Color32 {
558    /// Mostly for debug purposes, this is a decent way to log or inspect the color in debug mode. Looks like
559    /// “Color32{r: 255, g: 255, b: 255, a: 255}”
560    /// <https://stereokit.net/Pages/StereoKit/Color/ToString.html>  
561    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
562        write!(f, "r:{}, g:{}, b:{}, a:{}", self.r, self.g, self.b, self.a)
563    }
564}
565
566/// Named colors <https://www.w3.org/wiki/CSS/Properties/color/keywords>
567pub mod named_colors {
568    use super::Color32;
569
570    // Basic :
571    /// Black color rgb(0, 0, 0)
572    pub const BLACK: Color32 = Color32::rgb(0, 0, 0);
573    /// silver color rgb(192, 192, 192)
574    pub const SILVER: Color32 = Color32::rgb(192, 192, 192);
575    /// gray color rgb(128, 128, 128)
576    pub const GRAY: Color32 = Color32::rgb(128, 128, 128);
577    /// white color rgb(255, 255, 255)
578    pub const WHITE: Color32 = Color32::rgb(255, 255, 255);
579    /// maroon color rgb(128, 0, 0)
580    pub const MAROON: Color32 = Color32::rgb(128, 0, 0);
581    /// red color rgb(255, 0, 0)
582    pub const RED: Color32 = Color32::rgb(255, 0, 0);
583    /// purple color rgb(128, 0, 128)
584    pub const PURPLE: Color32 = Color32::rgb(128, 0, 128);
585    /// fuchsia color rgb(255, 0, 255)
586    pub const FUCHSIA: Color32 = Color32::rgb(255, 0, 255);
587    /// green color rgb(0, 128, 0)
588    pub const GREEN: Color32 = Color32::rgb(0, 128, 0);
589    /// lime color rgb(0, 255, 0)
590    pub const LIME: Color32 = Color32::rgb(0, 255, 0);
591    /// olive color rgb(128, 128, 0)
592    pub const OLIVE: Color32 = Color32::rgb(128, 128, 0);
593    /// yellow color rgb(255, 255, 0)
594    pub const YELLOW: Color32 = Color32::rgb(255, 255, 0);
595    /// navy color rgb(0, 0, 128)
596    pub const NAVY: Color32 = Color32::rgb(0, 0, 128);
597    /// blue color rgb(0, 0, 255)
598    pub const BLUE: Color32 = Color32::rgb(0, 0, 255);
599    /// teal color rgb(0, 128, 128)
600    pub const TEAL: Color32 = Color32::rgb(0, 128, 128);
601    /// aqua color rgb(0, 255, 255)
602    pub const AQUA: Color32 = Color32::rgb(0, 255, 255);
603
604    //Extended
605    /// alice blue color rgb(240, 248, 255)
606    pub const ALICE_BLUE: Color32 = Color32::rgb(240, 248, 255);
607    /// antique white color rgb(250, 235, 215)
608    pub const ANTIQUE_WHITE: Color32 = Color32::rgb(250, 235, 215);
609    /// aquamarine color rgb(127, 255, 212)
610    pub const AQUAMARINE: Color32 = Color32::rgb(127, 255, 212);
611    /// azure color rgb(240, 255, 255)
612    pub const AZURE: Color32 = Color32::rgb(240, 255, 255);
613    /// beige color rgb(255, 228, 196)
614    pub const BEIGE: Color32 = Color32::rgb(255, 228, 196);
615    /// bisque color rgb(255, 228, 196)
616    pub const BISQUE: Color32 = Color32::rgb(255, 228, 196);
617    /// blanched almond color rgb(255, 235, 205)
618    pub const BLANCHED_ALMOND: Color32 = Color32::rgb(255, 235, 205);
619    /// blue violet color rgb(138, 43, 226)
620    pub const BLUE_VIOLET: Color32 = Color32::rgb(138, 43, 226);
621    /// brown color rgb(165, 42, 42)
622    pub const BROWN: Color32 = Color32::rgb(165, 42, 42);
623    /// burlywood color rgb(222, 184, 135)
624    pub const BURLY_WOOD: Color32 = Color32::rgb(222, 184, 135);
625    /// cadet blue color rgb(95, 158, 160)
626    pub const CADET_BLUE: Color32 = Color32::rgb(95, 158, 160);
627    /// chartreuse color rgb(127, 255, 0)
628    pub const CHARTREUSE: Color32 = Color32::rgb(127, 255, 0);
629    /// chocolate color rgb(210, 105, 30)
630    pub const CHOCOLATE: Color32 = Color32::rgb(210, 105, 30);
631    /// coral color rgb(255, 127, 80)
632    pub const CORAL: Color32 = Color32::rgb(255, 127, 80);
633    /// cornflower blue color rgb(100, 149, 237)
634    pub const CORNFLOWER_BLUE: Color32 = Color32::rgb(100, 149, 237);
635    /// cornsilk color rgb(255, 248, 220)
636    pub const CORN_SILK: Color32 = Color32::rgb(255, 248, 220);
637    /// crimson color rgb(220, 20, 60)
638    pub const CRIMSON: Color32 = Color32::rgb(220, 20, 60);
639    /// cyan color rgb(0, 255, 255)
640    pub const CYAN: Color32 = Color32::rgb(0, 255, 255);
641    /// dark blue color rgb(0, 0, 139)
642    pub const DARK_BLUE: Color32 = Color32::rgb(0, 0, 139);
643    /// dark cyan color rgb(0, 139, 139)
644    pub const DARK_CYAN: Color32 = Color32::rgb(0, 139, 139);
645    /// dark goldenrod color rgb(184, 134, 11)
646    pub const DARK_GOLDEN_ROD: Color32 = Color32::rgb(184, 134, 11);
647    /// dark gray color rgb(169, 169, 169)
648    pub const DARK_GRAY: Color32 = Color32::rgb(169, 169, 169);
649    /// dark green color rgb(0, 100, 0)
650    pub const DARK_GREEN: Color32 = Color32::rgb(0, 100, 0);
651    /// dark grey color rgb(169, 169, 169)
652    pub const DARK_GREY: Color32 = Color32::rgb(169, 169, 169);
653    /// dark khaki color rgb(189, 183, 107)
654    pub const DARK_KHAKI: Color32 = Color32::rgb(189, 183, 107);
655    /// dark magenta color rgb(139, 0, 139)
656    pub const DARK_MAGENTA: Color32 = Color32::rgb(139, 0, 139);
657    /// dark olive green color rgb(85, 107, 47)
658    pub const DARK_OLIVE_GREEN: Color32 = Color32::rgb(85, 107, 47);
659    /// dark orange color rgb(255, 140, 0)
660    pub const DARK_ORANGE: Color32 = Color32::rgb(255, 140, 0);
661    /// dark orchid color rgb(153, 50, 204)
662    pub const DARK_ORCHID: Color32 = Color32::rgb(153, 50, 204);
663    /// dark red color rgb(139, 0, 0)
664    pub const DARK_RED: Color32 = Color32::rgb(139, 0, 0);
665    /// dark salmon color rgb(233, 150, 122)
666    pub const DARK_SALMON: Color32 = Color32::rgb(233, 150, 122);
667    /// dark sea green color rgb(143, 188, 143)
668    pub const DARK_SEA_GREEN: Color32 = Color32::rgb(143, 188, 143);
669    /// dark slate blue color rgb(72, 61, 139)
670    pub const DARK_SLATE_BLUE: Color32 = Color32::rgb(72, 61, 139);
671    /// dark slate gray color rgb(47, 79, 79)
672    pub const DARK_SLATE_GRAY: Color32 = Color32::rgb(47, 79, 79);
673    /// dark slate grey color rgb(47, 79, 79)
674    pub const DARK_SLATE_GREY: Color32 = Color32::rgb(47, 79, 79);
675    /// dark turquoise color rgb(0, 206, 209)
676    pub const DARK_TURQUOISE: Color32 = Color32::rgb(0, 206, 209);
677    /// dark violet color rgb(148, 0, 211)
678    pub const DARK_VIOLET: Color32 = Color32::rgb(148, 0, 211);
679    /// deep pink color rgb(255, 20, 147)
680    pub const DEEP_PINK: Color32 = Color32::rgb(255, 20, 147);
681    /// deep sky blue color rgb(0, 191, 255)
682    pub const DEEP_SKY_BLUE: Color32 = Color32::rgb(0, 191, 255);
683    /// dim gray color rgb(105, 105, 105)
684    pub const DIM_GRAY: Color32 = Color32::rgb(105, 105, 105);
685    /// dim grey color rgb(105, 105, 105)
686    pub const DIM_GREY: Color32 = Color32::rgb(105, 105, 105);
687    /// dodger blue color rgb(30, 144, 255)
688    pub const DOGER_BLUE: Color32 = Color32::rgb(30, 144, 255);
689    /// fire brick color rgb(178, 34, 34)
690    pub const FIRE_BRICK: Color32 = Color32::rgb(178, 34, 34);
691    /// floral white color rgb(255, 250, 240)
692    pub const FLORAL_WHITE: Color32 = Color32::rgb(255, 250, 240);
693    /// forest green color rgb(34, 139, 34)
694    pub const FOREST_GREEN: Color32 = Color32::rgb(34, 139, 34);
695    /// gainsboro color rgb(220, 220, 220)
696    pub const GAINSBORO: Color32 = Color32::rgb(220, 220, 220);
697    /// ghost white color rgb(248, 248, 255)
698    pub const GHOST_WHITE: Color32 = Color32::rgb(248, 248, 255);
699    /// gold color rgb(255, 215, 0)
700    pub const GOLD: Color32 = Color32::rgb(255, 215, 0);
701    /// goldenrod color rgb(218, 165, 32)
702    pub const GOLDENROD: Color32 = Color32::rgb(218, 165, 32);
703    /// green yellow color rgb(173, 255, 47)
704    pub const GREEN_YELLOW: Color32 = Color32::rgb(173, 255, 47);
705    /// grey color rgb(128, 128, 128)
706    pub const GREY: Color32 = Color32::rgb(128, 128, 128);
707    /// honeydew color rgb(240, 255, 240)
708    pub const HONEYDEW: Color32 = Color32::rgb(240, 255, 240);
709    /// hot pink color rgb(255, 105, 180)
710    pub const HOT_PINK: Color32 = Color32::rgb(255, 105, 180);
711    /// indian red color rgb(205, 92, 92)
712    pub const INDIAN_RED: Color32 = Color32::rgb(205, 92, 92);
713    /// indigo color rgb(75, 0, 130)
714    pub const INDIGO: Color32 = Color32::rgb(75, 0, 130);
715    /// ivory color rgb(255, 255, 240)
716    pub const IVORY: Color32 = Color32::rgb(255, 255, 240);
717    /// khaki color rgb(240, 230, 140)
718    pub const KHAKI: Color32 = Color32::rgb(240, 230, 140);
719    /// lavender color rgb(230, 230, 250)
720    pub const LAVENDER: Color32 = Color32::rgb(230, 230, 250);
721    /// lavender blush color rgb(255, 240, 245)
722    pub const LAVENDER_BLUSH: Color32 = Color32::rgb(255, 240, 245);
723    /// lawn green color rgb(124, 242, 0)
724    pub const LAWN_GREEN: Color32 = Color32::rgb(124, 242, 0);
725    /// lemon chiffon color rgb(255, 250, 205)
726    pub const LEMON_CHIFFON: Color32 = Color32::rgb(255, 250, 205);
727    /// light blue color rgb(173, 216, 230)
728    pub const LIGHT_BLUE: Color32 = Color32::rgb(173, 216, 230);
729    /// light coral color rgb(240, 128, 128)
730    pub const LIGHT_CORAL: Color32 = Color32::rgb(240, 128, 128);
731    /// light cyan color rgb(224, 255, 255)
732    pub const LIGHT_CYAN: Color32 = Color32::rgb(224, 255, 255);
733    /// light goldenrod yellow color rgb(250, 250, 210)
734    pub const LIGHT_GOLDENROD_YELLOW: Color32 = Color32::rgb(250, 250, 210);
735    /// light gray color rgb(211, 211, 211)
736    pub const LIGHT_GRAY: Color32 = Color32::rgb(211, 211, 211);
737    /// light green color rgb(144, 238, 144)
738    pub const LIGHT_GREEN: Color32 = Color32::rgb(144, 238, 144);
739    /// light grey color rgb(211, 211, 211)
740    pub const LIGHT_GREY: Color32 = Color32::rgb(211, 211, 211);
741    /// light pink color rgb(255, 182, 193)
742    pub const LIGHT_PINK: Color32 = Color32::rgb(255, 182, 193);
743    /// light salmon color rgb(255, 160, 122)
744    pub const LIGHT_SALMON: Color32 = Color32::rgb(255, 160, 122);
745    /// light sea green color rgb(32, 178, 170)
746    pub const LIGHT_SEA_GREEN: Color32 = Color32::rgb(32, 178, 170);
747    /// light sky blue color rgb(135, 206, 250)
748    pub const LIGHT_SKY_BLUE: Color32 = Color32::rgb(135, 206, 250);
749    /// light slate gray color rgb(119, 136, 153)
750    pub const LIGHT_SLATE_GRAY: Color32 = Color32::rgb(119, 136, 153);
751    /// light steel blue color rgb(176, 196, 222)
752    pub const LIGHT_STEEL_BLUE: Color32 = Color32::rgb(176, 196, 222);
753    /// light yellow color rgb(255, 255, 224)
754    pub const LIGHT_YELLOW: Color32 = Color32::rgb(255, 255, 224);
755    /// lime green color rgb(50, 205, 50)
756    pub const LIME_GREEN: Color32 = Color32::rgb(50, 205, 50);
757    /// linen color rgb(250, 240, 230)
758    pub const LINEN: Color32 = Color32::rgb(250, 240, 230);
759    /// magenta color rgb(255, 0, 255)
760    pub const MAGENTA: Color32 = Color32::rgb(255, 0, 255);
761    /// medium aquamarine color rgb(102, 205, 170)
762    pub const MEDIUM_AQUAMARINE: Color32 = Color32::rgb(102, 205, 170);
763    /// medium blue color rgb(0, 0, 205)
764    pub const MEDIUM_BLUE: Color32 = Color32::rgb(0, 0, 205);
765    /// medium orchid color rgb(186, 85, 211)
766    pub const MEDIUM_ORCHID: Color32 = Color32::rgb(186, 85, 211);
767    /// medium purple color rgb(147, 112, 219)
768    pub const MEDIUM_PURPLE: Color32 = Color32::rgb(147, 112, 219);
769    /// medium sea green color rgb(60, 179, 113)
770    pub const MEDIUM_SEA_GREEN: Color32 = Color32::rgb(60, 179, 113);
771    /// medium slate blue color rgb(123, 104, 238)
772    pub const MEDIUM_SLATE_BLUE: Color32 = Color32::rgb(123, 104, 238);
773    /// medium spring green color rgb(0, 250, 154)
774    pub const MEDIUM_SPRING_GREEN: Color32 = Color32::rgb(0, 250, 154);
775    /// medium turquoise color rgb(72, 209, 204)
776    pub const MEDIUM_TURQUOISE: Color32 = Color32::rgb(72, 209, 204);
777    /// medium violet red color rgb(199, 21, 133)
778    pub const MEDIUM_VIOLET_RED: Color32 = Color32::rgb(199, 21, 133);
779    /// midnight blue color rgb(25, 25, 112)
780    pub const MIDNIGHT_BLUE: Color32 = Color32::rgb(25, 25, 112);
781    /// mint cream color rgb(245, 255, 250)
782    pub const MINT_CREAM: Color32 = Color32::rgb(245, 255, 250);
783    /// misty rose color rgb(255, 228, 225)
784    pub const MISTY_ROSE: Color32 = Color32::rgb(255, 228, 225);
785    /// moccasin color rgb(255, 228, 181)
786    pub const MOCCASIN: Color32 = Color32::rgb(255, 228, 181);
787    /// navajo white color rgb(255, 222, 173)
788    pub const NAVAJO_WHITE: Color32 = Color32::rgb(255, 222, 173);
789    /// old lace color rgb(253, 245, 230)
790    pub const OLD_LACE: Color32 = Color32::rgb(253, 245, 230);
791    /// olive drab color rgb(107, 142, 35)
792    pub const OLIVE_DRAB: Color32 = Color32::rgb(107, 142, 35);
793    /// orange color rgb(255, 165, 0)
794    pub const ORANGE: Color32 = Color32::rgb(255, 165, 0);
795    /// orange red color rgb(255, 69, 0)
796    pub const ORANGE_RED: Color32 = Color32::rgb(255, 69, 0);
797    /// orchid color rgb(218, 112, 214)
798    pub const ORCHID: Color32 = Color32::rgb(218, 112, 214);
799    /// pale golden rod color rgb(238, 232, 170)
800    pub const PALE_GOLDEN_ROD: Color32 = Color32::rgb(238, 232, 170);
801    /// pale green color rgb(152, 251, 152)
802    pub const PALE_GREEN: Color32 = Color32::rgb(152, 251, 152);
803    /// pale turquoise color rgb(175, 238, 238)
804    pub const PALE_TURQUOISE: Color32 = Color32::rgb(175, 238, 238);
805    /// pale violet red color rgb(219, 112, 147)
806    pub const PALE_VIOLET_RED: Color32 = Color32::rgb(219, 112, 147);
807    /// papaya whip color rgb(255, 239, 213)
808    pub const PAPAYAWHIP: Color32 = Color32::rgb(255, 239, 213);
809    /// peach puff color rgb(255, 218, 185)
810    pub const PEACH_PUFF: Color32 = Color32::rgb(255, 218, 185);
811    /// peru color rgb(205, 133, 63)
812    pub const PERU: Color32 = Color32::rgb(205, 133, 63);
813    /// pink color rgb(255, 192, 203)
814    pub const PINK: Color32 = Color32::rgb(255, 192, 203);
815    /// plum color rgb(221, 160, 221)
816    pub const PLUM: Color32 = Color32::rgb(221, 160, 221);
817    /// powder blue color rgb(176, 224, 230)
818    pub const POWDER_BLUE: Color32 = Color32::rgb(176, 224, 230);
819    /// rosy brown color rgb(188, 143, 143)
820    pub const ROSY_BROWN: Color32 = Color32::rgb(188, 143, 143);
821    /// royal blue color rgb(65, 105, 225)
822    pub const ROYAL_BLUE: Color32 = Color32::rgb(65, 105, 225);
823    /// saddle brown color rgb(139, 69, 19)
824    pub const SADDLE_BROWN: Color32 = Color32::rgb(139, 69, 19);
825    /// salmon color rgb(250, 128, 114)
826    pub const SALMON: Color32 = Color32::rgb(250, 128, 114);
827    /// sandy brown color rgb(244, 164, 96)
828    pub const SANDY_BROWN: Color32 = Color32::rgb(244, 164, 96);
829    /// sea green color rgb(46, 139, 87)
830    pub const SEA_GREEN: Color32 = Color32::rgb(46, 139, 87);
831    /// sea shell color rgb(255, 245, 238)
832    pub const SEA_SHELL: Color32 = Color32::rgb(255, 245, 238);
833    /// sienna color rgb(160, 82, 45)
834    pub const SIENNA: Color32 = Color32::rgb(160, 82, 45);
835    /// sky blue color rgb(135, 206, 235)
836    pub const SKY_BLUE: Color32 = Color32::rgb(135, 206, 235);
837    /// slate blue color rgb(106, 90, 205)
838    pub const SLATE_BLUE: Color32 = Color32::rgb(106, 90, 205);
839    /// slate gray color rgb(112, 128, 144)
840    pub const SLATE_GRAY: Color32 = Color32::rgb(112, 128, 144);
841    /// slate grey color rgb(112, 128, 144)
842    pub const SLATE_GREY: Color32 = Color32::rgb(112, 128, 144);
843    /// snow color rgb(255, 250, 250)
844    pub const SNOW: Color32 = Color32::rgb(255, 250, 250);
845    /// spring green color rgb(0, 255, 127)
846    pub const SPRING_GREEN: Color32 = Color32::rgb(0, 255, 127);
847    /// steel blue color rgb(70, 130, 180)
848    pub const STEEL_BLUE: Color32 = Color32::rgb(70, 130, 180);
849    /// tan color rgb(210, 180, 140)
850    pub const TAN: Color32 = Color32::rgb(210, 180, 140);
851    /// thistle color rgb(216, 191, 216)
852    pub const THISTLE: Color32 = Color32::rgb(216, 191, 216);
853    /// tomato color rgb(255, 99, 71)
854    pub const TOMATO: Color32 = Color32::rgb(255, 99, 71);
855    /// turquoise color rgb(64, 224, 208)
856    pub const TURQUOISE: Color32 = Color32::rgb(64, 224, 208);
857    /// violet color rgb(238, 130, 238)
858    pub const VIOLET: Color32 = Color32::rgb(238, 130, 238);
859    /// wheat color rgb(245, 222, 179)
860    pub const WHEAT: Color32 = Color32::rgb(245, 222, 179);
861    /// white smoke color rgb(245, 245, 245)
862    pub const WHITE_SMOKE: Color32 = Color32::rgb(245, 245, 245);
863    /// yellow green color rgb(154, 205, 50)
864    pub const YELLOW_GREEN: Color32 = Color32::rgb(154, 205, 50);
865}
866
867/// What type of user motion is the device capable of tracking? For the normal fully capable XR headset, this should be
868/// 6dof (rotation and translation), but more limited headsets may be restricted to 3dof (rotation) and flatscreen
869/// computers with the simulator off would be none.
870/// <https://stereokit.net/Pages/StereoKit/DeviceTracking.html>
871///
872/// see [`Device::get_tracking`]
873#[derive(Debug, Copy, Clone, PartialEq, Eq)]
874#[repr(u32)]
875pub enum DeviceTracking {
876    /// No tracking is available! This is likely a flatscreen application, not an XR applicaion.
877    None = 0,
878    /// This tracks rotation only, this may be a limited device without tracking cameras, or could be a more capable
879    /// headset in a 3dof mode. DoF stands for Degrees of Freedom.
880    Dof3 = 1,
881    /// This is capable of tracking both the position and rotation of the device, most fully featured XR headsets
882    /// (such as a HoloLens 2) will have this. DoF stands for Degrees of Freedom.
883    Dof6 = 2,
884}
885
886/// This describes a type of display hardware!
887/// <https://stereokit.net/Pages/StereoKit/DisplayType.html>
888///
889/// see [`Device::get_display_type`]
890#[derive(Debug, Copy, Clone, PartialEq, Eq)]
891#[repr(u32)]
892pub enum DisplayType {
893    /// Not a display at all, or the variable hasn’t been initialized properly yet.
894    None = 0,
895    /// This is a stereo display! It has 2 screens, or two sections that display content in stereo, one for each eye.
896    /// This could be a VR headset, or like a 3D tv.
897    Stereo = 1,
898    /// This is a single flat screen, with no stereo depth. This could be something like either a computer monitor, or a
899    /// phone with passthrough AR.
900    Flatscreen = 2,
901}
902
903/// TODO: waiting for C# implementation
904/// <https://stereokit.net/Pages/StereoKit/FovInfo.html>
905#[derive(Debug, Copy, Clone)]
906#[repr(C)]
907pub struct FovInfo {
908    pub left: f32,
909    pub right: f32,
910    pub top: f32,
911    pub bottom: f32,
912}
913
914/// This class describes the device that is running this application! It contains identifiers, capabilities, and a few
915/// adjustable settings here and there.
916/// <https://stereokit.net/Pages/StereoKit/Device.html>
917///
918/// ### Examples
919/// ```
920/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
921/// use stereokit_rust::{util::{Device, DisplayType, DeviceTracking}, sk::DisplayBlend};
922///
923/// // These are the expected results for tests on a PC:
924/// let display_type = Device::get_display_type();
925/// assert_eq!(display_type, DisplayType::Flatscreen);
926///
927/// let display_blend = Device::get_display_blend();
928/// assert_eq!(display_blend, DisplayBlend::Opaque);
929///
930/// let device_tracking = Device::get_tracking();
931/// assert_eq!(device_tracking, DeviceTracking::None);
932///
933/// let device_name = Device::get_name().unwrap();
934/// assert_eq!(device_name, "Offscreen");
935///
936/// let device_runtime = Device::get_runtime().unwrap();
937/// assert_eq!(device_runtime, "None");
938///
939/// let device_gpu = Device::get_gpu().unwrap();
940/// assert_ne!(device_gpu, "Name of your GPU");
941///
942/// assert_eq!(Device::has_eye_gaze(), false);
943/// assert_eq!(Device::has_hand_tracking(), false);
944///
945/// assert_eq!(Device::valid_blend(DisplayBlend::None), false);
946/// assert_eq!(Device::display_blend(DisplayBlend::None), false);
947/// ```
948pub struct Device;
949
950unsafe extern "C" {
951    pub fn device_display_get_type() -> DisplayType;
952    pub fn device_display_get_blend() -> DisplayBlend;
953    pub fn device_display_set_blend(blend: DisplayBlend) -> Bool32T;
954    pub fn device_display_valid_blend(blend: DisplayBlend) -> Bool32T;
955    pub fn device_display_get_refresh_rate() -> f32;
956    pub fn device_display_get_width() -> i32;
957    pub fn device_display_get_height() -> i32;
958    pub fn device_display_get_fov() -> FovInfo;
959    pub fn device_get_tracking() -> DeviceTracking;
960    pub fn device_get_name() -> *const c_char;
961    pub fn device_get_runtime() -> *const c_char;
962    pub fn device_get_gpu() -> *const c_char;
963    pub fn device_has_eye_gaze() -> Bool32T;
964    pub fn device_has_hand_tracking() -> Bool32T;
965}
966
967impl Device {
968    /// Allows you to set and get the current blend mode of the device! Setting this may not succeed if the blend mode
969    /// is not valid.
970    /// <https://stereokit.net/Pages/StereoKit/Device/DisplayBlend.html>
971    ///
972    /// see also [`device_display_set_blend`]
973    /// ### Examples
974    /// ```
975    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
976    /// use stereokit_rust::{util::Device, sk::DisplayBlend};
977    ///
978    /// // These are the expected results for tests on a PC:
979    /// assert_eq!(Device::get_display_blend(), DisplayBlend::Opaque);
980    ///
981    /// assert_eq!(Device::display_blend(DisplayBlend::AnyTransparent), false);
982    /// assert_eq!(Device::display_blend(DisplayBlend::None), false);
983    /// assert_eq!(Device::display_blend(DisplayBlend::Additive), false);
984    /// ```
985    pub fn display_blend(blend: DisplayBlend) -> bool {
986        unsafe { device_display_set_blend(blend) != 0 }
987    }
988
989    /// Allows you to set and get the current blend mode of the device! Setting this may not succeed if the blend mode
990    /// is not valid.
991    /// <https://stereokit.net/Pages/StereoKit/Device/DisplayBlend.html>
992    ///
993    /// see also [`device_display_get_blend`]
994    /// see example in [`Device::display_blend`]
995    pub fn get_display_blend() -> DisplayBlend {
996        unsafe { device_display_get_blend() }
997    }
998
999    /// What type of display is this? Most XR headsets will report stereo, but the Simulator will report flatscreen.
1000    /// <https://stereokit.net/Pages/StereoKit/Device/DisplayType.html>
1001    ///
1002    /// see also [`device_display_get_type`]
1003    /// ### Examples
1004    /// ```
1005    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1006    /// use stereokit_rust::util::{Device, DisplayType};
1007    ///
1008    /// // These are the expected results for tests on a PC:
1009    /// assert_eq!(Device::get_display_type(), DisplayType::Flatscreen);
1010    /// ```
1011    pub fn get_display_type() -> DisplayType {
1012        unsafe { device_display_get_type() }
1013    }
1014
1015    /// This is the name of the OpenXR runtime that powers the current device! This can help you determine which
1016    /// implementation quirks to expect based on the codebase used. On the simulator, this will be "Simulator", and in
1017    /// other non-XR modes this will be "None".
1018    /// <https://stereokit.net/Pages/StereoKit/Device/Runtime.html>
1019    ///
1020    /// see also [`device_get_runtime`]
1021    /// ### Examples
1022    /// ```
1023    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1024    /// use stereokit_rust::util::Device;
1025    ///
1026    /// // These are the expected results for tests on a PC:
1027    /// assert_eq!(Device::get_runtime().unwrap(), "None");
1028    /// ```
1029    pub fn get_runtime<'a>() -> Result<&'a str, StereoKitError> {
1030        unsafe { CStr::from_ptr(device_get_runtime()) }
1031            .to_str()
1032            .map_err(|e| StereoKitError::CStrError(e.to_string()))
1033    }
1034
1035    /// The reported name of the GPU, this will differ between D3D and GL.
1036    /// <https://stereokit.net/Pages/StereoKit/Device/GPU.html>
1037    ///
1038    /// see also [`device_get_gpu`]
1039    pub fn get_gpu<'a>() -> Result<&'a str, StereoKitError> {
1040        unsafe { CStr::from_ptr(device_get_gpu()) }
1041            .to_str()
1042            .map_err(|e| StereoKitError::CStrError(e.to_string()))
1043    }
1044
1045    /// Does the device we’re on have eye tracking support present for input purposes? This is not an indicator that the
1046    /// user has given the application permission to access this information. See Input.Gaze for how to use this data.
1047    /// <https://stereokit.net/Pages/StereoKit/Device/HasEyeGaze.html>
1048    ///
1049    /// see also [`device_has_eye_gaze`]
1050    /// ### Examples
1051    /// ```
1052    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1053    /// use stereokit_rust::util::Device;
1054    ///
1055    /// // These are the expected results for tests on a PC:
1056    /// assert_eq!(Device::has_eye_gaze(), false);
1057    /// ```
1058    pub fn has_eye_gaze() -> bool {
1059        unsafe { device_has_eye_gaze() != 0 }
1060    }
1061
1062    /// Tells if the device is capable of tracking hands. This does not tell if the user is actually using their hands
1063    /// for input, merely that it’s possible to!
1064    /// <https://stereokit.net/Pages/StereoKit/Device/HasHandTracking.html>
1065    ///
1066    /// see also [`device_has_hand_tracking`]
1067    /// ### Examples
1068    /// ```
1069    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1070    /// use stereokit_rust::util::Device;
1071    ///
1072    /// // These are the expected results for tests on a PC:
1073    /// assert_eq!(Device::has_hand_tracking(), false);
1074    /// ```
1075    pub fn has_hand_tracking() -> bool {
1076        unsafe { device_has_hand_tracking() != 0 }
1077    }
1078
1079    /// This is the name of the active device! From OpenXR, this is the same as systemName from XrSystemProperties. The
1080    /// simulator will say “Simulator”.
1081    /// <https://stereokit.net/Pages/StereoKit/Device/Name.html>
1082    ///
1083    /// see also [`device_get_name`]
1084    /// ### Examples
1085    /// ```
1086    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1087    /// use stereokit_rust::util::Device;
1088    ///
1089    /// // These are the expected results for tests on a PC:
1090    /// assert_eq!(Device::get_name().unwrap(), "Offscreen");
1091    /// ```
1092    pub fn get_name<'a>() -> Result<&'a str, StereoKitError> {
1093        unsafe { CStr::from_ptr(device_get_name()) }
1094            .to_str()
1095            .map_err(|e| StereoKitError::CStrError(e.to_string()))
1096    }
1097
1098    /// The tracking capabilities of this device! Is it 3DoF, rotation only? Or is it 6DoF, with positional tracking as
1099    /// well? Maybe it can’t track at all!
1100    /// <https://stereokit.net/Pages/StereoKit/Device/Tracking.html>
1101    ///
1102    /// see also [`device_get_tracking`]
1103    /// ### Examples
1104    /// ```
1105    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1106    /// use stereokit_rust::util::{Device, DeviceTracking};
1107    ///
1108    /// // These are the expected results for tests on a PC:
1109    /// assert_eq!(Device::get_tracking(), DeviceTracking::None);
1110    /// ```
1111    pub fn get_tracking() -> DeviceTracking {
1112        unsafe { device_get_tracking() }
1113    }
1114
1115    /// Tells if a particular blend mode is valid on this device. Some devices may be capable of more than one blend
1116    /// mode.
1117    /// <https://stereokit.net/Pages/StereoKit/Device/ValidBlend.html>
1118    ///
1119    /// see also [`device_display_valid_blend`]
1120    /// ### Examples
1121    /// ```
1122    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1123    /// use stereokit_rust::{util::Device, sk::DisplayBlend};
1124    ///
1125    /// // These are the expected results for tests on a PC:
1126    /// assert_eq!(Device::valid_blend(DisplayBlend::Opaque), true);
1127    /// assert_eq!(Device::valid_blend(DisplayBlend::None), false);
1128    /// assert_eq!(Device::valid_blend(DisplayBlend::Additive), false);
1129    /// assert_eq!(Device::valid_blend(DisplayBlend::Blend), false);
1130    /// assert_eq!(Device::valid_blend(DisplayBlend::AnyTransparent), false);
1131    /// ```
1132    pub fn valid_blend(blend: DisplayBlend) -> bool {
1133        unsafe { device_display_valid_blend(blend) != 0 }
1134    }
1135}
1136
1137/// A color/position pair for Gradient values!
1138/// <https://stereokit.net/Pages/StereoKit/GradientKey.html>
1139///
1140/// see [`Gradient`]
1141/// ### Examples
1142/// ```
1143/// use stereokit_rust::util::{GradientKey, Color128, named_colors};
1144///
1145/// let key0 = GradientKey::new(named_colors::GOLD, 0.75);
1146/// let key1 = GradientKey::new(   Color128::new(1.0, 0.84313726, 0.0, 1.0), 0.75);
1147/// let key2 = GradientKey{ color: Color128::new(1.0, 0.84313726, 0.0, 1.0), position: 0.75 };
1148///
1149/// assert_eq!(key0, key1);
1150/// assert_eq!(key1, key2);
1151/// ```
1152#[derive(Debug, Copy, Clone, PartialEq)]
1153#[repr(C)]
1154pub struct GradientKey {
1155    /// The color for this item, preferably in some form of linear color space. Gamma corrected colors will definitely
1156    /// not math correctly.
1157    pub color: Color128,
1158    /// Typically a value between 0-1! This is the position of the color along the ‘x-axis’ of the gradient.
1159    pub position: f32,
1160}
1161
1162impl GradientKey {
1163    /// A basic copy constructor for GradientKey.
1164    /// <https://stereokit.net/Pages/StereoKit/GradientKey/GradientKey.html>
1165    /// * `color_linear` - The color for this item, preferably in some form of linear color space. Gamma corrected
1166    ///   colors will definitely not math correctly.
1167    /// * `position` - Typically a value between 0-1! This is the position of the color along the ‘x-axis’ of the
1168    ///   gradient.
1169    pub fn new(color_linear: impl Into<Color128>, position: f32) -> Self {
1170        Self { color: color_linear.into(), position }
1171    }
1172}
1173
1174/// A Gradient is a sparse collection of color keys that are used to represent a ramp of colors! This class is largely
1175/// just storing colors and allowing you to sample between them.
1176///
1177/// Since the Gradient is just interpolating values, you can use whatever color space you want here, as long as it's
1178/// linear and not gamma! Gamma space RGB can't math properly at all. It can be RGB(linear), HSV, LAB, just remember
1179/// which one you have, and be sure to convert it appropriately later. Data is stored as float colors, so this'll be a
1180/// high accuracy blend!
1181/// <https://stereokit.net/Pages/StereoKit/Gradient.html>
1182///
1183/// see also [GradientKey] [`crate::tex::Tex::gen_particle`] [`crate::tex::SHCubemap::gen_cubemap_gradient`]
1184/// ### Examples
1185/// ```
1186/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1187/// use stereokit_rust::{maths::Vec3, system::AssetState, tex::{Tex, SHCubemap},
1188///                      util::{named_colors, Gradient, GradientKey, Color128}};
1189///
1190/// let keys = [
1191///     GradientKey::new(Color128::BLACK_TRANSPARENT, 0.0),
1192///     GradientKey::new(named_colors::RED, 0.1),
1193///     GradientKey::new(named_colors::CYAN, 0.4),
1194///     GradientKey::new(named_colors::BLUE, 0.5),
1195///     GradientKey::new(Color128::BLACK, 0.7)];
1196///
1197/// let sh_cubemap = SHCubemap::gen_cubemap_gradient(Gradient::new(Some(&keys)),
1198///                                                  Vec3::UP, 128);
1199/// sh_cubemap.render_as_sky();
1200///
1201/// let mut gradient = Gradient::new(None);
1202/// gradient
1203///     .add(Color128::BLACK_TRANSPARENT, 0.0)
1204///     .add(named_colors::YELLOW, 0.1)
1205///     .add(named_colors::LIGHT_BLUE, 0.4)
1206///     .add(named_colors::BLUE, 0.5)
1207///     .add(Color128::BLACK, 0.7);
1208/// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1209/// ```
1210pub struct Gradient(pub NonNull<_GradientT>);
1211
1212impl Drop for Gradient {
1213    fn drop(&mut self) {
1214        unsafe { gradient_destroy(self.0.as_ptr()) };
1215    }
1216}
1217
1218impl AsRef<Gradient> for Gradient {
1219    fn as_ref(&self) -> &Gradient {
1220        self
1221    }
1222}
1223
1224/// StereoKit internal type.
1225#[repr(C)]
1226#[derive(Debug)]
1227pub struct _GradientT {
1228    _unused: [u8; 0],
1229}
1230
1231/// StereoKit ffi type.
1232pub type GradientT = *mut _GradientT;
1233
1234unsafe extern "C" {
1235    pub fn gradient_create() -> GradientT;
1236    pub fn gradient_create_keys(in_arr_keys: *const GradientKey, count: i32) -> GradientT;
1237    pub fn gradient_add(gradient: GradientT, color_linear: Color128, position: f32);
1238    pub fn gradient_set(gradient: GradientT, index: i32, color_linear: Color128, position: f32);
1239    pub fn gradient_remove(gradient: GradientT, index: i32);
1240    pub fn gradient_count(gradient: GradientT) -> i32;
1241    pub fn gradient_get(gradient: GradientT, at: f32) -> Color128;
1242    pub fn gradient_get32(gradient: GradientT, at: f32) -> Color32;
1243    pub fn gradient_destroy(gradient: GradientT);
1244}
1245impl Gradient {
1246    /// Creates a new, completely empty gradient.
1247    /// <https://stereokit.net/Pages/StereoKit/Gradient/Gradient.html>
1248    /// * `keys` - These can be in any order that you like, they’ll be sorted by their GradientKey.position value
1249    ///   regardless!
1250    ///
1251    /// see also [`gradient_create`] [`gradient_create_keys`]
1252    /// ### Examples
1253    /// ```
1254    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1255    /// use stereokit_rust::{maths::Vec3, tex::{Tex, SHCubemap},
1256    ///                      util::{named_colors, Gradient, GradientKey, Color128}};
1257    ///
1258    /// let keys = [
1259    ///     GradientKey::new(Color128::BLACK_TRANSPARENT, 0.0),
1260    ///     GradientKey::new(named_colors::RED, 0.5),
1261    ///     GradientKey::new(Color128::BLACK, 0.7)];
1262    ///
1263    /// let gradient1 = Gradient::new(Some(&keys));
1264    /// assert_eq!(gradient1.get_count(), 3);
1265    /// let sh_cubemap = SHCubemap::gen_cubemap_gradient(gradient1, Vec3::UP, 16);
1266    /// sh_cubemap.render_as_sky();
1267    ///
1268    /// let mut gradient2 = Gradient::new(Some(&keys));
1269    /// gradient2.add(named_colors::CYAN, 0.4);
1270    /// assert_eq!(gradient2.get_count(), 4);
1271    /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient2));
1272    /// ```
1273    pub fn new(keys: Option<&[GradientKey]>) -> Self {
1274        match keys {
1275            Some(keys) => {
1276                Gradient(NonNull::new(unsafe { gradient_create_keys(keys.as_ptr(), keys.len() as i32) }).unwrap())
1277            }
1278            None => Gradient(NonNull::new(unsafe { gradient_create() }).unwrap()),
1279        }
1280    }
1281
1282    /// This adds a color key into the list. It’ll get inserted to the right slot based on its position.
1283    /// <https://stereokit.net/Pages/StereoKit/Gradient/Add.html>
1284    /// * `color_linear` - Any linear space color you like!
1285    /// * `position` - Typically a value between 0-1! This is the position of the color along the ‘x-axis’ of the
1286    ///   gradient.
1287    ///
1288    /// see also [`gradient_add`]
1289    /// ### Examples
1290    /// ```
1291    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1292    /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128}};
1293    ///
1294    /// let mut gradient = Gradient::new(None);
1295    /// gradient
1296    ///     .add(Color128::BLACK_TRANSPARENT, 0.0)
1297    ///     .add(named_colors::YELLOW, 0.1)
1298    ///     .add(named_colors::LIGHT_BLUE, 0.4)
1299    ///     .add(named_colors::BLUE, 0.5)
1300    ///     .add(Color128::BLACK, 0.7);
1301    /// assert_eq!(gradient.get_count(), 5);
1302    /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1303    /// ```
1304    pub fn add(&mut self, color_linear: impl Into<Color128>, position: f32) -> &mut Self {
1305        unsafe { gradient_add(self.0.as_ptr(), color_linear.into(), position) };
1306        self
1307    }
1308
1309    /// Updates the color key at the given index! This will NOT re-order color keys if they are moved past another
1310    /// key’s position, which could lead to strange behavior.
1311    /// <https://stereokit.net/Pages/StereoKit/Gradient/Set.html>
1312    /// * `index` - Index of the color key to change.
1313    /// * `color_linear` - Any linear space color you like!
1314    /// * `position` - Typically a value between 0-1! This is the position of the color along the ‘x-axis’ of the
1315    ///   gradient.
1316    ///
1317    /// see also [`gradient_set`]
1318    /// ### Examples
1319    /// ```
1320    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1321    /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient,  Color128}};
1322    ///
1323    /// let mut gradient = Gradient::new(None);
1324    /// gradient
1325    ///     .add(Color128::BLACK_TRANSPARENT, 0.0)
1326    ///     .add(named_colors::YELLOW, 0.1)
1327    ///     .add(named_colors::LIGHT_BLUE, 0.4)
1328    ///     .add(named_colors::BLUE, 0.5)
1329    ///     .add(Color128::BLACK, 0.7);
1330    /// assert_eq!(gradient.get_count(), 5);
1331    /// assert_eq!(gradient.get(0.3), Color128 { r: 0.7856209, g: 0.8980392, b: 0.6013072, a: 1.0 });
1332    ///
1333    /// gradient.set(2, named_colors::RED, 0.3);
1334    /// gradient.set(-20, named_colors::RED, -10.3); // out of bounds, should do nothing
1335    /// assert_eq!(gradient.get_count(), 5);
1336    /// assert_eq!(gradient.get(0.3), named_colors::RED.into());
1337    ///
1338    /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1339    /// ```
1340    pub fn set(&mut self, index: i32, color_linear: impl Into<Color128>, position: f32) -> &mut Self {
1341        if index < 0 || index >= self.get_count() {
1342            return self;
1343        }
1344        unsafe { gradient_set(self.0.as_ptr(), index, color_linear.into(), position) };
1345        self
1346    }
1347
1348    /// Removes the color key at the given index! This won't reindex the gradient so get_count will still return the
1349    /// same value.
1350    /// <https://stereokit.net/Pages/StereoKit/Gradient/Remove.html>
1351    /// * `index` - The index of the color key to remove.
1352    ///
1353    /// see also [`gradient_remove`]
1354    /// ### Examples
1355    /// ```
1356    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1357    /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128}};
1358    ///
1359    /// let mut gradient = Gradient::new(None);
1360    /// gradient
1361    ///     .add(Color128::BLACK_TRANSPARENT, 0.0)
1362    ///     .add(named_colors::YELLOW, 0.1)
1363    ///     .add(named_colors::LIGHT_BLUE, 0.4)
1364    ///     .add(named_colors::BLUE, 0.5)
1365    ///     .add(Color128::BLACK, 0.7);
1366    /// assert_eq!(gradient.get_count(), 5);
1367    /// assert_eq!(gradient.get(0.4), named_colors::LIGHT_BLUE.into());
1368    ///
1369    /// gradient.remove(2);
1370    /// gradient.remove(19).remove(-189);
1371    /// assert_eq!(gradient.get_count(), 5);
1372    /// assert_eq!(gradient.get(0.4), Color128 { r: 0.25, g: 0.25, b: 0.75, a: 1.0 });
1373    ///
1374    /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1375    /// ```
1376    pub fn remove(&mut self, index: i32) -> &mut Self {
1377        if index < 0 || index >= self.get_count() {
1378            return self;
1379        }
1380        unsafe { gradient_remove(self.0.as_ptr(), index) };
1381        self
1382    }
1383
1384    /// The number of color keys present in this gradient.
1385    /// <https://stereokit.net/Pages/StereoKit/Gradient/Count.html>
1386    ///
1387    /// see also [`gradient_count`]
1388    /// ### Examples
1389    /// ```
1390    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1391    /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128}};
1392    ///
1393    /// let mut gradient = Gradient::new(None);
1394    /// assert_eq!(gradient.get_count(), 0);
1395    ///
1396    /// gradient
1397    ///     .add(Color128::BLACK_TRANSPARENT, 0.0)
1398    ///     .add(named_colors::YELLOW, 0.1)
1399    ///     .add(Color128::BLACK, 0.7);
1400    /// assert_eq!(gradient.get_count(), 3);
1401    ///
1402    /// gradient.remove(1);
1403    /// assert_eq!(gradient.get_count(), 3);
1404    ///
1405    /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1406    ///```
1407    pub fn get_count(&self) -> i32 {
1408        unsafe { gradient_count(self.0.as_ptr()) }
1409    }
1410
1411    /// Samples the gradient’s color at the given position!
1412    /// <https://stereokit.net/Pages/StereoKit/Gradient/Get.html>
1413    /// * `at` - Typically a value between 0-1, but if you used larger or smaller values for your color key’s positions,
1414    ///   it’ll be in that range!
1415    ///
1416    /// Returns the interpolated color at the given position. If ‘at’ is smaller or larger than the gradient’s position
1417    /// range, then the color will be clamped to the color at the beginning or end of the gradient!
1418    /// see also [`gradient_get`]
1419    /// ### Examples
1420    /// ```
1421    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1422    /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128}};
1423    ///
1424    /// let mut gradient = Gradient::new(None);
1425    /// gradient
1426    ///     .add(Color128::BLACK_TRANSPARENT, 0.0)
1427    ///     .add(named_colors::YELLOW, 0.1)
1428    ///     .add(named_colors::LIGHT_BLUE, 0.4)
1429    ///     .add(named_colors::BLUE, 0.5)
1430    ///     .add(Color128::BLACK, 0.7);
1431    ///
1432    /// assert_eq!(gradient.get(0.3), Color128 { r: 0.7856209, g: 0.8980392, b: 0.6013072, a: 1.0 });
1433    /// assert_eq!(gradient.get(0.4), named_colors::LIGHT_BLUE.into());
1434    /// assert_eq!(gradient.get(0.5), named_colors::BLUE.into());
1435    ///
1436    /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1437    /// ```
1438    pub fn get(&self, at: f32) -> Color128 {
1439        unsafe { gradient_get(self.0.as_ptr(), at) }
1440    }
1441
1442    /// Samples the gradient’s color at the given position, and converts it to a 32 bit color. If your RGBA color
1443    /// values are outside of the 0-1 range, then you’ll get some issues as they’re converted to 0-255 range bytes!
1444    /// <https://stereokit.net/Pages/StereoKit/Gradient/Remove.html>
1445    /// * `at` - Typically a value between 0-1, but if you used larger or smaller values for your color key’s positions,
1446    ///   it’ll be in that range!
1447    ///
1448    /// Returns the interpolated 32 bit color at the given position. If ‘at’ is smaller or larger than the gradient’s
1449    /// position range, then the color will be clamped to the color at the beginning or end of the gradient!
1450    /// see also [`gradient_get32`]
1451    /// ### Examples
1452    /// ```
1453    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1454    /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128, Color32}};
1455    ///
1456    /// let mut gradient = Gradient::new(None);
1457    /// gradient
1458    ///     .add(Color128::BLACK_TRANSPARENT, 0.0)
1459    ///     .add(named_colors::YELLOW, 0.1)
1460    ///     .add(named_colors::LIGHT_BLUE, 0.4)
1461    ///     .add(named_colors::BLUE, 0.5)
1462    ///     .add(Color128::BLACK, 0.7);
1463    ///
1464    /// assert_eq!(gradient.get32(0.3), Color32 { r: 200, g: 229, b: 153, a: 255 });
1465    /// assert_eq!(gradient.get32(0.4), named_colors::LIGHT_BLUE);
1466    /// assert_eq!(gradient.get32(0.5), named_colors::BLUE);
1467    ///
1468    /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1469    /// ```
1470    pub fn get32(&self, at: f32) -> Color32 {
1471        unsafe { gradient_get32(self.0.as_ptr(), at) }
1472    }
1473}
1474
1475/// file extension to filter ie ".txt" ".gltf"
1476/// <https://stereokit.net/Pages/StereoKit/Platform.html>
1477///
1478/// see also [`crate::system::Assets::MODEL_FORMATS`] [`crate::system::Assets::TEXTURE_FORMATS`]
1479#[derive(Debug, Default, Copy, Clone)]
1480#[repr(C)]
1481pub struct FileFilter {
1482    ext: [c_char; 32usize],
1483}
1484
1485impl FileFilter {
1486    pub fn new(str: impl AsRef<str>) -> Self {
1487        let mut value: [c_char; 32usize] = [0; 32usize];
1488        let c_array = CString::new(str.as_ref()).unwrap();
1489        let c_array = c_array.as_bytes_with_nul();
1490
1491        let mut c_array_iter = c_array.iter().map(|v| *v as c_char);
1492        value[0..c_array.len()].fill_with(|| c_array_iter.next().unwrap());
1493
1494        Self { ext: value }
1495    }
1496}
1497
1498/// This class contains some tools for hashing data within StereoKit! Certain systems in StereoKit use string hashes
1499/// instead of full strings for faster search and compare, like UI and Materials, so this class gives access to the code
1500/// SK uses for hashing. StereoKit currently internally uses a 64 bit FNV hash, though this detail should be pretty
1501/// transparent to developers
1502pub struct Hash;
1503
1504unsafe extern "C" {
1505    pub fn hash_string(str_utf8: *const c_char) -> IdHashT;
1506    pub fn hash_string_with(str_utf8: *const c_char, root: IdHashT) -> IdHashT;
1507    pub fn hash_int(val: i32) -> IdHashT;
1508    pub fn hash_int_with(val: i32, root: IdHashT) -> IdHashT;
1509}
1510
1511impl Hash {
1512    /// This will hash the UTF8 representation of the given string into a hash value that StereoKit can use.
1513    /// <https://stereokit.net/Pages/StereoKit/Hash/String.html>
1514    /// * `str` - A string (UTF8),that will be hashed.
1515    ///
1516    /// Returns a StereoKit hash representing the provided string.
1517    /// see also [`hash_string`] [`Hash::string_with`]
1518    /// ### Examples
1519    /// ```
1520    /// use stereokit_rust::util::Hash;
1521    ///
1522    /// let hash = Hash::string("Hello World");
1523    /// assert_eq!(hash, 4420528118743043111);
1524    /// ```
1525    pub fn string(str: impl AsRef<str>) -> IdHashT {
1526        let c_str = CString::new(str.as_ref()).unwrap();
1527        unsafe { hash_string(c_str.as_ptr()) }
1528    }
1529
1530    /// This will hash the UTF8 representation of the given string into a hash value that StereoKit can use. This
1531    /// overload allows you to combine your hash with an existing hash.
1532    /// <https://stereokit.net/Pages/StereoKit/Hash/String.html>
1533    /// * `str` - A string (UTF8),that will be hashed.
1534    /// * `root` - The hash value this new hash will start from.
1535    ///
1536    /// Returns a StereoKit hash representing a combination of the provided string and the root hash.
1537    /// see also [`hash_string_with`] [`Hash::string`]
1538    /// ### Examples
1539    /// ```
1540    /// use stereokit_rust::util::Hash;
1541    ///
1542    /// let hash1 = Hash::string("Hello");
1543    /// assert_eq!(hash1, 7201466553693376363);
1544    ///
1545    /// let hash2 = Hash::string_with(" World", hash1);
1546    /// assert_eq!(hash2, 4420528118743043111);
1547    /// ```
1548    pub fn string_with(str: impl AsRef<str>, root: IdHashT) -> IdHashT {
1549        let c_str = CString::new(str.as_ref()).unwrap();
1550        unsafe { hash_string_with(c_str.as_ptr(), root) }
1551    }
1552
1553    /// This will hash an integer into a hash value that StereoKit can use. This is helpful for adding in some
1554    /// uniqueness using something like a for loop index. This may be best when combined with additional hashes.
1555    /// <https://stereokit.net/Pages/StereoKit/Hash/Int.html>
1556    /// * `val` - An integer that will be hashed.
1557    ///
1558    /// Returns a StereoKit hash representing the provided integer.
1559    /// see also [`hash_int`] [`Hash::int_with`]
1560    /// ### Examples
1561    /// ```
1562    /// use stereokit_rust::util::Hash;
1563    ///
1564    /// let hash = Hash::int(123456789);
1565    /// assert_eq!(hash, 8379007418144316681);
1566    /// ```
1567    pub fn int(val: i32) -> IdHashT {
1568        unsafe { hash_int(val) }
1569    }
1570
1571    /// This will hash an integer into a hash value that StereoKit can use. This is helpful for adding in some
1572    /// uniqueness using something like a for loop index. This overload allows you to combine your hash with an existing
1573    /// hash.
1574    /// <https://stereokit.net/Pages/StereoKit/Hash/Int.html>
1575    /// * `val` - An integer that will be hashed.
1576    /// * `root` - The hash value this new hash will start from.
1577    ///
1578    /// Returns a StereoKit hash representing a combination of the provided string and the root hash.
1579    /// see also [`hash_int_with`] [`Hash::int`]
1580    /// ### Examples
1581    /// ```
1582    /// use stereokit_rust::util::Hash;
1583    ///
1584    /// let hash1 = Hash::int(123456789);
1585    /// assert_eq!(hash1, 8379007418144316681);
1586    ///
1587    /// let hash2 = Hash::int_with(123456789, hash1);
1588    /// assert_eq!(hash2, 13864748593440029765);
1589    /// ```
1590    pub fn int_with(int: i32, root: IdHashT) -> IdHashT {
1591        unsafe { hash_int_with(int, root) }
1592    }
1593}
1594
1595/// TODO: UNSTABLE: When opening the Platform.FilePicker, this enum describes how the picker should look and behave.
1596/// <https://stereokit.net/Pages/StereoKit/PickerMode.html>
1597#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1598#[repr(u32)]
1599pub enum PickerMode {
1600    /// Allow opening a single file.
1601    Open = 0,
1602    /// Allow the user to enter or select the name of the destination file.
1603    Save = 1,
1604}
1605
1606/// This class provides some platform related code that runs cross-platform. You might be able to do many of these
1607/// things with rust or C#, but you might not be able to do them in as a portable manner as these methods do!
1608/// <https://stereokit.net/Pages/StereoKit/Platform.html>
1609///
1610/// ### Examples
1611/// ```
1612/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1613/// use stereokit_rust::{util::{Platform}, system::TextContext};
1614///
1615/// Platform::keyboard_show(true, TextContext::Text);
1616///
1617/// let mut path = std::env::current_dir().expect("Current directory should be readable");
1618/// path.push("assets/textures/");
1619/// assert!(path.is_dir());
1620/// path.push("readme.md");
1621/// assert!(path.is_file());
1622///
1623/// let file_content = Platform::read_file_text(&path)
1624///                                  .expect("File should be readable");
1625/// assert!(file_content.starts_with("# Images "));
1626///
1627/// assert!(Platform::write_file_text(path, file_content).is_ok());
1628/// ```
1629pub struct Platform;
1630
1631unsafe extern "C" {
1632    pub fn platform_file_picker(
1633        mode: PickerMode,
1634        callback_data: *mut c_void,
1635        picker_callback: ::std::option::Option<
1636            unsafe extern "C" fn(callback_data: *mut c_void, confirmed: Bool32T, filename: *const c_char),
1637        >,
1638        filters: *const FileFilter,
1639        filter_count: i32,
1640    );
1641    pub fn platform_file_picker_sz(
1642        mode: PickerMode,
1643        callback_data: *mut c_void,
1644        picker_callback_sz: ::std::option::Option<
1645            unsafe extern "C" fn(
1646                callback_data: *mut c_void,
1647                confirmed: Bool32T,
1648                filename_ptr: *const c_char,
1649                filename_length: i32,
1650            ),
1651        >,
1652        in_arr_filters: *const FileFilter,
1653        filter_count: i32,
1654    );
1655    pub fn platform_file_picker_close();
1656    pub fn platform_file_picker_visible() -> Bool32T;
1657    pub fn platform_read_file(
1658        filename_utf8: *const c_char,
1659        out_data: *mut *mut c_void,
1660        out_size: *mut usize,
1661    ) -> Bool32T;
1662    pub fn platform_write_file(filename_utf8: *const c_char, data: *mut c_void, size: usize) -> Bool32T;
1663    pub fn platform_write_file_text(filename_utf8: *const c_char, text_utf8: *const c_char) -> Bool32T;
1664    pub fn platform_keyboard_get_force_fallback() -> Bool32T;
1665    pub fn platform_keyboard_set_force_fallback(force_fallback: Bool32T);
1666    pub fn platform_keyboard_show(visible: Bool32T, type_: TextContext);
1667    pub fn platform_keyboard_visible() -> Bool32T;
1668    pub fn platform_keyboard_set_layout(
1669        type_: TextContext,
1670        keyboard_layout: *const *const c_char,
1671        layouts_num: i32,
1672    ) -> Bool32T;
1673}
1674
1675/// File_picker trampoline
1676///
1677/// see also [`Plaform::file_picker`]
1678unsafe extern "C" fn fp_trampoline<FS: FnMut(&str), FC: FnMut()>(
1679    user_data: *mut c_void,
1680    confirmed: Bool32T,
1681    filename: *const c_char,
1682) {
1683    let data = unsafe { &mut *(user_data as *mut (&mut FS, &mut FC)) };
1684    let (update, cancel) = data;
1685    if confirmed != 0 {
1686        let c_str = unsafe { CStr::from_ptr(filename).to_str().unwrap() };
1687        update(c_str)
1688    } else {
1689        cancel()
1690    }
1691}
1692
1693/// File_picker_sz trampoline
1694///
1695/// see also [`Plaform::file_picker`]
1696unsafe extern "C" fn fp_sz_trampoline<F: FnMut(bool, &str)>(
1697    user_data: *mut c_void,
1698    confirmed: Bool32T,
1699    filename: *const c_char,
1700    filename_length: i32,
1701) {
1702    let closure = unsafe { &mut *(user_data as *mut &mut F) };
1703    if confirmed != 0 && filename_length > 0 {
1704        let c_str = unsafe { CStr::from_ptr(filename).to_str().unwrap() };
1705        closure(true, c_str)
1706    } else {
1707        let c_str = "";
1708        closure(false, c_str)
1709    }
1710}
1711
1712impl Platform {
1713    /// Force the use of StereoKit’s built-in fallback keyboard instead of the system keyboard. This may be great for
1714    /// testing or look and feel matching, but the system keyboard should generally be preferred for accessibility
1715    /// reasons.
1716    /// <https://stereokit.net/Pages/StereoKit/Platform/ForceFallbackKeyboard.html>
1717    ///
1718    /// see also [`platform_keyboard_set_force_fallback`]
1719    /// ### Examples
1720    /// ```
1721    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1722    /// use stereokit_rust::util::Platform;
1723    ///
1724    /// assert_eq!(Platform::get_force_fallback_keyboard(), false);
1725    ///
1726    /// Platform::force_fallback_keyboard(true);
1727    /// assert_eq!(Platform::get_force_fallback_keyboard(), true);
1728    ///
1729    /// Platform::force_fallback_keyboard(false);
1730    /// assert_eq!(Platform::get_force_fallback_keyboard(), false);
1731    /// ```
1732    pub fn force_fallback_keyboard(force_fallback: bool) {
1733        unsafe { platform_keyboard_set_force_fallback(force_fallback as Bool32T) }
1734    }
1735
1736    /// TODO: UNSTABLE: Starts a file picker window! This will create a native file picker window if one is available in the current
1737    /// setup, and if it is not, it’ll create a fallback filepicker build using StereoKit’s UI.
1738    ///
1739    /// Flatscreen apps will show traditional file pickers, and UWP has an OS provided file picker that works in MR. All
1740    /// others currently use the fallback system.
1741    ///
1742    /// A note for UWP apps, UWP generally does not have permission to access random files, unless the user has chosen
1743    /// them with the picker! This picker properly handles permissions for individual files on UWP, but may have issues
1744    /// with files that reference other files, such as .gltf files with external textures. See [`Platform::write_file`]
1745    /// and [`Platform.read_file`] for manually reading and writing files in a cross-platfom manner.
1746    /// <https://stereokit.net/Pages/StereoKit/Platform/FilePicker.html>
1747    /// * `mode` - Are we trying to Open a file, or Save a file? This changes the appearance and behavior of the picker
1748    ///   to support the specified action.
1749    /// * `on_select_file` - This Action will be called with the proper filename when the picker has successfully
1750    ///   completed! On a cancel or close event, this Action is not called.
1751    /// * `on_cancel` - If the user cancels the file picker, or the picker is closed via FilePickerClose, this Action is
1752    ///   called.
1753    /// * `filters` - A list of file extensions that the picker should filter for. This is in the format of “.glb” and
1754    ///   is case insensitive.
1755    ///
1756    /// see also [`platform_file_picker`]
1757    pub fn file_picker<FS: FnMut(&str), FC: FnMut()>(
1758        mode: PickerMode,
1759        mut on_select_file: FS,
1760        mut on_cancel: FC,
1761        filters: &[impl AsRef<str>],
1762    ) {
1763        let mut c_filters = Vec::new();
1764        for filter in filters {
1765            c_filters.push(FileFilter::new(filter));
1766        }
1767
1768        let mut closure = (&mut on_select_file, &mut on_cancel);
1769        unsafe {
1770            platform_file_picker(
1771                mode,
1772                &mut closure as *mut _ as *mut c_void,
1773                Some(fp_trampoline::<FS, FC>),
1774                c_filters.as_slice().as_ptr(),
1775                c_filters.len() as i32,
1776            )
1777        }
1778    }
1779
1780    /// TODO: UNSTABLE: Starts a file picker window! This will create a native file picker window if one is available in the current
1781    /// setup, and if it is not, it’ll create a fallback filepicker build using StereoKit’s UI.
1782    ///
1783    /// Flatscreen apps will show traditional file pickers, and UWP has an OS provided file picker that works in MR. All
1784    /// others currently use the fallback system. Some pickers will block the system and return right away, but others
1785    /// will stick around and let users continue to interact with the app.
1786    ///
1787    /// A note for UWP apps, UWP generally does not have permission to access random files, unless the user has chosen
1788    /// them with the picker! This picker properly handles permissions for individual files on UWP, but may have issues
1789    /// with files that reference other files, such as .gltf files with external textures. See [`Platform::write_file`]
1790    /// and [`Platform.read_file`] for manually reading and writing files in a cross-platfom manner.
1791    /// <https://stereokit.net/Pages/StereoKit/Platform/FilePicker.html>
1792    /// * `mode` - Are we trying to Open a file, or Save a file? This changes the appearance and behavior of the picker
1793    ///   to support the specified action.
1794    /// * on_complete - This action will be called when the file picker has finished, either via a cancel event, or from
1795    ///   a confirm event. First parameter is a bool, where true indicates the presence of a valid filename, and false
1796    ///   indicates a failure or cancel event.
1797    /// * `filters` - A list of file extensions that the picker should filter for. This is in the format of “.glb” and
1798    ///   is case insensitive.
1799    ///
1800    /// see also [`platform_file_picker_sz`]
1801    pub fn file_picker_sz<F: FnMut(bool, &str)>(mode: PickerMode, mut on_complete: F, filters: &[impl AsRef<str>]) {
1802        let mut c_filters = Vec::new();
1803        for filter in filters {
1804            c_filters.push(FileFilter::new(filter));
1805        }
1806
1807        let mut closure = &mut on_complete;
1808        unsafe {
1809            platform_file_picker_sz(
1810                mode,
1811                &mut closure as *mut _ as *mut c_void,
1812                Some(fp_sz_trampoline::<F>),
1813                c_filters.as_slice().as_ptr(),
1814                c_filters.len() as i32,
1815            )
1816        }
1817    }
1818
1819    /// TODO: UNSTABLE: If the picker is visible, this will close it and immediately trigger a cancel event for the active picker.
1820    /// <https://stereokit.net/Pages/StereoKit/Platform/FilePickerClose.html>
1821    ///
1822    /// see also [`platform_file_picker_close`]
1823    pub fn file_picker_close() {
1824        unsafe { platform_file_picker_close() }
1825    }
1826
1827    /// Request or hide a soft keyboard for the user to type on. StereoKit will surface OS provided soft keyboards where
1828    /// available, and use a fallback keyboard when not. On systems with physical keyboards, soft keyboards generally
1829    /// will not be shown if the user has interacted with their physical keyboard recently.
1830    /// <https://stereokit.net/Pages/StereoKit/Platform/KeyboardShow.html>
1831    /// * `show` - Tells whether or not to open or close the soft keyboard.
1832    /// * `input_type` - Soft keyboards can change layout to optimize for the type of text that’s required. StereoKit
1833    ///   will request the soft keyboard layout that most closely represents the TextContext provided.
1834    ///
1835    /// see also [`platform_keyboard_show`]
1836    /// ### Examples
1837    /// ```
1838    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1839    /// use stereokit_rust::{util::Platform, system::TextContext};
1840    ///
1841    /// // On desktop, this will not show the keyboard, as it is assumed that the user has a physical keyboard.
1842    /// Platform::keyboard_show(true, TextContext::Text);
1843    /// assert_eq!(Platform::is_keyboard_visible(), false);
1844    ///
1845    /// Platform::keyboard_show(false, TextContext::Text);
1846    /// assert_eq!(Platform::is_keyboard_visible(), false);
1847    /// ```
1848    pub fn keyboard_show(show: bool, input_type: TextContext) {
1849        unsafe { platform_keyboard_show(show as Bool32T, input_type) }
1850    }
1851
1852    /// Replace the default keyboard type with a custom layout.
1853    /// <https://stereokit.net/Pages/StereoKit/Platform/KeyboardSetLayout.html>
1854    /// * `keyboard_type` - The type of keyboard to replace.
1855    /// * `keyboard_layouts` - Custom keyboard layout to replace the defualt layout.
1856    ///
1857    /// Returns `true` if keyboard type was swapped with the provided layout.
1858    /// see also [`platform_keyboard_set_layout`]
1859    /// ### Examples
1860    /// ```
1861    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1862    /// use stereokit_rust::{util::Platform, system::TextContext};
1863    ///
1864    /// pub const FR_KEY_TEXT: &str = r#"²|&|é|"|'|(|\-|è|_|ç|à|)|=|{|}|spr:sk/ui/backspace-\b-8-3|spr:sk/ui/close----close
1865    /// Tab-\t-9-3|a|z|e|r|t|y|u|i|o|p|^|$|[|]|\|
1866    /// Entrée-\n-13-4|q|s|d|f|g|h|j|k|l|m|ù|*|#|Entrée-\n-13-3
1867    /// spr:sk/ui/shift--16-3-go_1|<|w|x|c|v|b|n|,|;|:|!|`|@|spr:sk/ui/shift--16-2-go_1|spr:sk/ui/arrow_up--38
1868    /// Ctrl--17-4-mod|Cmd--91-3|Alt--18-3-go_2| - -32-13|Alt--18-3-go_2|Ctrl--17-3-mod|spr:sk/ui/arrow_left--37|spr:sk/ui/arrow_down--40|spr:sk/ui/arrow_right--39|"#;
1869    ///
1870    /// pub const FR_KEY_TEXT_SHIFT: &str = r#"@|1|2|3|4|5|6|7|8|9|0|°|+|Æ|Œ|spr:sk/ui/backspace-\b-8-3|spr:sk/ui/close----close
1871    /// Tab-\t-9-3|A|Z|E|R|T|Y|U|I|O|P|¨|£|Ê|É|È
1872    /// Entrée-\n-13-4|Q|S|D|F|G|H|J|K|L|M|%|µ|Ç|Entrée-\n-13-3
1873    /// spr:sk/ui/shift--16-3-go_0|>|W|X|C|V|B|N|?|.|/|§|À|Ô|spr:sk/ui/shift--16-2-go_0|spr:sk/ui/arrow_up--38
1874    /// Ctrl--17-4-mod|Cmd--91-3|Alt--18-3-go_2| - -32-13|Alt--18-3-go_2|Ctrl--17-3-mod|spr:sk/ui/arrow_left--37|spr:sk/ui/arrow_down--40|spr:sk/ui/arrow_right--39|"#;
1875    ///
1876    /// pub const FR_KEY_TEXT_ALT: &str = r#"*|/|~|#|{|[|\||`|\\|^|@|]|}|æ|œ|spr:sk/ui/backspace-\b-8-3|spr:sk/ui/close----close
1877    /// Tab-\t-9-3|à|â|ä|ç|é|è|ê|ë|î|ï|ô|ö|«|»|¤
1878    /// Entrée-\n-13-4|ù|û|ü|ÿ|À|Â|Ä|Ç|É|È|Ê|Ë|%|Entrée-\n-13-3
1879    /// spr:sk/ui/shift--16-3-go_1|Î|Ï|Ô|Ö|Ù|Û|Ü|Ÿ|$|£|€|¥|✋|spr:sk/ui/shift--16-2-go_1|spr:sk/ui/arrow_up--38
1880    /// Ctrl--17-4-mod|Cmd--91-3|Alt--18-3-go_0| - -32-13|Alt--18-3-go_0|Ctrl--17-3-mod|spr:sk/ui/arrow_left--37|spr:sk/ui/arrow_down--40|spr:sk/ui/arrow_right--39|"#;
1881    ///
1882    /// let keyboard_layouts = vec![FR_KEY_TEXT, FR_KEY_TEXT_SHIFT, FR_KEY_TEXT_ALT];
1883    ///
1884    /// assert_eq!(Platform::keyboard_set_layout(TextContext::Text, &keyboard_layouts), true);
1885    /// ```
1886    pub fn keyboard_set_layout(keyboard_type: TextContext, keyboard_layouts: &Vec<&str>) -> bool {
1887        let mut keyboard_layouts_c = vec![];
1888        for str in keyboard_layouts {
1889            let c_str = CString::new(*str).unwrap().into_raw() as *const c_char;
1890            keyboard_layouts_c.push(c_str);
1891        }
1892        unsafe {
1893            platform_keyboard_set_layout(
1894                keyboard_type,
1895                keyboard_layouts_c.as_slice().as_ptr(),
1896                keyboard_layouts_c.len() as i32,
1897            ) != 0
1898        }
1899    }
1900
1901    /// Reads the entire contents of the file as a UTF-8 string, taking advantage of any permissions that may have been
1902    /// granted by Platform::file_picker(_sz?). Returns Err on failure.
1903    /// <https://stereokit.net/Pages/StereoKit/Platform/ReadFileText.html>
1904    /// * `filename` - The path to the file to read.
1905    ///
1906    /// see also [`platform_read_file`]
1907    /// ### Examples
1908    /// ```
1909    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1910    /// use stereokit_rust::{util::{Platform}, system::TextContext};
1911    ///
1912    /// let mut path = std::env::current_dir().expect("Current directory should be readable");
1913    /// path.push("config.toml");
1914    /// assert!(path.is_file());
1915    ///
1916    /// let file_content = Platform::read_file_text(&path)
1917    ///                                  .expect("File should be readable");
1918    /// assert!(file_content.starts_with("[env]"));
1919    /// ```
1920    pub fn read_file_text<'a>(filename: impl AsRef<Path>) -> Result<&'a str, StereoKitError> {
1921        let path_buf = filename.as_ref().to_path_buf();
1922        let c_str = CString::new(
1923            path_buf
1924                .clone()
1925                .to_str() //
1926                .ok_or(StereoKitError::ReadFileError(path_buf.clone(), "Failed to convert path to string".into()))?,
1927        )?;
1928        let out_data = CString::new("H")?.into_raw() as *mut *mut c_void;
1929        let mut len = 0usize;
1930        let len_ptr: *mut usize = &mut len;
1931        if unsafe { platform_read_file(c_str.as_ptr(), out_data, len_ptr) != 0 } {
1932            unsafe { CStr::from_ptr(*out_data as *const c_char) }
1933                .to_str()
1934                .map_err(|e| StereoKitError::ReadFileError(path_buf.clone(), e.to_string()))
1935        } else {
1936            Err(StereoKitError::ReadFileError(path_buf, "Failed to read file".into()))
1937        }
1938    }
1939
1940    /// Reads the entire contents of the file as a byte array, taking advantage of any permissions that may have been
1941    /// granted by Platform.FilePicker.
1942    /// <https://stereokit.net/Pages/StereoKit/Platform/ReadFile.html>
1943    /// * `filename` - The path to the file to read.
1944    ///
1945    /// see also [`platform_read_file`]
1946    /// ### Examples
1947    /// ```
1948    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1949    /// use stereokit_rust::{util::{Platform}, system::TextContext};
1950    ///
1951    /// let mut path = std::env::current_dir().expect("Current directory should be readable");
1952    /// path.push("assets/textures/");
1953    /// assert!(path.is_dir());
1954    /// path.push("readme.md");
1955    /// assert!(path.is_file());
1956    ///
1957    /// let file_content = Platform::read_file(&path)
1958    ///                                  .expect("File should be readable");
1959    /// assert!(file_content.starts_with(b"# Images "));
1960    /// ```
1961    pub fn read_file<'a>(filename: impl AsRef<Path>) -> Result<&'a [u8], StereoKitError> {
1962        let path_buf = filename.as_ref().to_path_buf();
1963        let c_str = CString::new(
1964            path_buf
1965                .clone()
1966                .to_str() //
1967                .ok_or(StereoKitError::ReadFileError(path_buf.clone(), "Failed to convert path to string".into()))?,
1968        )?;
1969        let out_data = CString::new("H")?.into_raw() as *mut *mut c_void;
1970        let mut len = 0usize;
1971        let len_ptr: *mut usize = &mut len;
1972        if unsafe { platform_read_file(c_str.as_ptr(), out_data, len_ptr) != 0 } {
1973            Ok(unsafe { std::slice::from_raw_parts(*out_data as *const u8, len) })
1974        } else {
1975            Err(StereoKitError::ReadFileError(path_buf, "Failed to read file".into()))
1976        }
1977    }
1978
1979    /// Writes a UTF-8 text file to the filesystem, taking advantage of any permissions that may have been granted by
1980    /// Platform::file_picker.
1981    /// <https://stereokit.net/Pages/StereoKit/Platform/WriteFile.html>
1982    /// * `filename` - The path to the file to write.
1983    /// * `text` -  A string to write to the file. This gets converted to a UTF-8 encoding.
1984    ///
1985    /// see also [`platform_write_file_text`]
1986    /// ### Examples
1987    /// ```
1988    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1989    /// use stereokit_rust::{util::{Platform}, system::TextContext};
1990    ///
1991    /// let mut path = std::env::current_dir().expect("Current directory should be readable");
1992    /// path.push("assets/icons/");
1993    /// assert!(path.is_dir());
1994    /// path.push("readme.md");
1995    /// assert!(path.is_file());
1996    ///
1997    /// let file_content = Platform::read_file_text(&path)
1998    ///                                  .expect("File should be readable");
1999    /// assert!(file_content.starts_with("# Images "));
2000    ///
2001    /// assert!(Platform::write_file_text(path, file_content).is_ok());
2002    /// ```
2003    pub fn write_file_text<S: AsRef<str>>(filename: impl AsRef<Path>, text: S) -> Result<bool, StereoKitError> {
2004        let path_buf = filename.as_ref().to_path_buf();
2005        let c_str = CString::new(
2006            path_buf
2007                .clone()
2008                .to_str() //
2009                .ok_or(StereoKitError::WriteFileError(path_buf.clone(), "Failed to convert path to string".into()))?,
2010        )?;
2011        let in_data = CString::new(text.as_ref())?.into_raw() as *const c_char;
2012        if unsafe { platform_write_file_text(c_str.as_ptr(), in_data) != 0 } {
2013            Ok(true)
2014        } else {
2015            Err(StereoKitError::WriteFileError(path_buf, "Failed to write file".into()))
2016        }
2017    }
2018
2019    /// Writes an array of bytes to the filesystem, taking advantage of any permissions that may have been granted by
2020    /// Platform::file_picker.
2021    /// <https://stereokit.net/Pages/StereoKit/Platform/WriteFile.html>
2022    /// * `filename` - The path to the file to write.
2023    /// * `data` - The data to write to the file.
2024    ///
2025    /// see also [`platform_write_file`]
2026    /// ### Examples
2027    /// ```
2028    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2029    /// use stereokit_rust::{util::{Platform}, system::TextContext};
2030    ///
2031    /// let mut path = std::env::current_dir().expect("Current directory should be readable");
2032    /// path.push("assets/icons/");
2033    /// assert!(path.is_dir());
2034    /// path.push("readme.md");
2035    /// assert!(path.is_file());
2036    ///
2037    /// let file_content = Platform::read_file(&path)
2038    ///                                  .expect("File should be readable");
2039    /// assert!(file_content.starts_with(b"# Images "));
2040    ///
2041    /// assert!(Platform::write_file(path, file_content).is_ok());
2042    /// ```
2043    pub fn write_file(filename: impl AsRef<Path>, data: &[u8]) -> Result<bool, StereoKitError> {
2044        let path_buf = filename.as_ref().to_path_buf();
2045        let c_str = CString::new(
2046            path_buf
2047                .clone()
2048                .to_str() //
2049                .ok_or(StereoKitError::WriteFileError(path_buf.clone(), "Failed to convert path to string".into()))?,
2050        )?;
2051        if unsafe { platform_write_file(c_str.as_ptr(), data.as_ptr() as *mut c_void, data.len()) != 0 } {
2052            Ok(true)
2053        } else {
2054            Err(StereoKitError::WriteFileError(path_buf, "Failed to write file".into()))
2055        }
2056    }
2057
2058    /// TODO: UNSTABLE: This will check if the file picker interface is currently visible. Some pickers will never show this, as they
2059    /// block the application until the picker has completed.
2060    /// <https://stereokit.net/Pages/StereoKit/Platform/FilePickerVisible.html>
2061    ///
2062    /// see also [`platform_file_picker_visible`]
2063    /// ### Examples
2064    /// ```
2065    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2066    /// use stereokit_rust::util::Platform;
2067    ///
2068    /// assert_eq!(Platform::get_file_picker_visible(), false);
2069    /// ```
2070    pub fn get_file_picker_visible() -> bool {
2071        unsafe { platform_file_picker_visible() != 0 }
2072    }
2073
2074    /// Force the use of StereoKit’s built-in fallback keyboard instead of the system keyboard. This may be great for
2075    /// testing or look and feel matching, but the system keyboard should generally be preferred for accessibility
2076    /// reasons.
2077    /// <https://stereokit.net/Pages/StereoKit/Platform/ForceFallbackKeyboard.html>
2078    ///
2079    /// see also [`platform_keyboard_get_force_fallback`]
2080    /// see example [`Platform::force_fallback_keyboard`]
2081    pub fn get_force_fallback_keyboard() -> bool {
2082        unsafe { platform_keyboard_get_force_fallback() != 0 }
2083    }
2084
2085    /// Check if a soft keyboard is currently visible. This may be an OS provided keyboard or StereoKit’s fallback
2086    /// keyboard, but will not indicate the presence of a physical keyboard.
2087    /// <https://stereokit.net/Pages/StereoKit/Platform/KeyboardVisible.html>
2088    ///
2089    /// see also [`platform_keyboard_visible`]
2090    /// see example [`Platform::keyboard_show`]
2091    pub fn is_keyboard_visible() -> bool {
2092        unsafe { platform_keyboard_visible() != 0 }
2093    }
2094}
2095
2096/// A light source used for creating SphericalHarmonics data.
2097/// <https://stereokit.net/Pages/StereoKit/SHLight.html>
2098///
2099/// see [`SphericalHarmonics`] see also [`crate::tex::SHCubemap`]
2100/// ### Examples
2101/// ```
2102/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2103/// use stereokit_rust::{maths::Vec3, tex::SHCubemap, util::{SHLight, named_colors, Color128}};
2104///
2105/// let light0 = SHLight::new([1.0, 0.2, 0.3], named_colors::RED);
2106/// let light1 = SHLight::new(Vec3::new(1.0, 0.2, 0.3), Color128::new(1.0, 0.0, 0.0, 1.0));
2107/// let light2 = SHLight{ dir_to: Vec3::new(1.0, 0.2, 0.3), color: named_colors::RED.into()};
2108///
2109/// assert_eq!(light0, light1);
2110/// assert_eq!(light1, light2);
2111/// ```
2112#[derive(Debug, Copy, Clone, PartialEq)]
2113#[repr(C)]
2114pub struct SHLight {
2115    /// Direction to the light source.
2116    pub dir_to: Vec3,
2117    /// Color of the light in linear space! Values here can exceed 1.
2118    pub color: Color128,
2119}
2120impl SHLight {
2121    /// A basic constructor for SHLight.
2122    /// <https://stereokit.net/Pages/StereoKit/SHLight.html>
2123    /// * `dir_to` - Direction to the light source.
2124    /// * `color` - Color of the light in linear space! Values here can exceed 1.
2125    pub fn new(dir_to: impl Into<Vec3>, color: impl Into<Color128>) -> Self {
2126        Self { dir_to: dir_to.into(), color: color.into() }
2127    }
2128}
2129/// Spherical Harmonics are kinda like Fourier, but on a sphere. That doesn’t mean terribly much to me, and could be
2130/// wrong, but check out here for more details about how Spherical Harmonics work in this context!
2131///
2132/// However, the more prctical thing is, SH can be a function that describes a value over the surface of a sphere! This
2133/// is particularly useful for lighting, since you can basically store the lighting information for a space in this
2134/// value! This is often used for lightmap data, or a light probe grid, but StereoKit just uses a single SH for the
2135/// entire scene. It’s a gross oversimplification, but looks quite good, and is really fast! That’s extremely great
2136/// when you’re trying to hit 60fps, or even 144fps.
2137/// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics.html>
2138///
2139/// see also: [`crate::tex::SHCubemap`]
2140/// ### Examples
2141/// ```
2142/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2143/// use stereokit_rust::{maths::Vec3,
2144///                      util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2145///
2146/// let light0 = SHLight::new([1.0, 0.0, 0.0], named_colors::RED);
2147/// let light1 = SHLight::new([0.0, 1.0, 0.0], named_colors::GREEN);
2148/// let light2 = SHLight::new([0.0, 0.0, 1.0], named_colors::BLUE);
2149///
2150/// let mut sh = SphericalHarmonics::from_lights(&[light0, light1, light2]);
2151/// sh.brightness(1.0)
2152///   .add(Vec3::NEG_Y, named_colors::GREEN)
2153///   .add(Vec3::NEG_Z, named_colors::BLUE)
2154///   .add(Vec3::NEG_X, named_colors::RED);
2155///
2156/// assert_eq!(sh.get_sample(Vec3::UP), Color128 { r: 0.5813507, g: 0.8046322, b: 0.5813487, a: 1.0 });
2157/// assert_eq!(sh.get_dominent_light_direction(), Vec3 { x: 0.27644092, y: 0.2728996, z: 0.9214696 });
2158/// ```
2159#[derive(Debug, Default, Copy, Clone, PartialEq)]
2160#[repr(C)]
2161pub struct SphericalHarmonics {
2162    pub coefficients: [Vec3; 9usize],
2163}
2164
2165unsafe extern "C" {
2166    pub fn sh_create(in_arr_lights: *const SHLight, light_count: i32) -> SphericalHarmonics;
2167    pub fn sh_brightness(ref_harmonics: *mut SphericalHarmonics, scale: f32);
2168    pub fn sh_add(ref_harmonics: *mut SphericalHarmonics, light_dir: Vec3, light_color: Vec3);
2169    pub fn sh_lookup(harmonics: *const SphericalHarmonics, normal: Vec3) -> Color128;
2170    pub fn sh_dominant_dir(harmonics: *const SphericalHarmonics) -> Vec3;
2171}
2172impl SphericalHarmonics {
2173    /// Creates a SphericalHarmonics approximation of the irradiance given from a set of directional lights!
2174    /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/FromLights.html>
2175    /// * `lights` - A list of directional lights!
2176    ///
2177    /// Returns a SphericalHarmonics approximation of the irradiance given from a set of directional lights!
2178    /// see also [`SHLight`]
2179    /// ### Examples
2180    /// ```
2181    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2182    /// use stereokit_rust::{maths::Vec3,
2183    ///                      util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2184    ///
2185    /// let light0 = SHLight::new([1.0, 1.0, 1.0], named_colors::RED);
2186    /// let light1 = SHLight::new([0.5, 0.5, 0.5], named_colors::RED);
2187    /// let light2 = SHLight::new([0.25, 0.25, 0.25], named_colors::RED);
2188    ///
2189    /// let sh = SphericalHarmonics::from_lights(&[light0, light1, light2]);
2190    ///
2191    /// assert_eq!(sh.get_sample(Vec3::UP), Color128 { r: 2.2098913, g: 0.0, b: 0.0, a: 1.0 });
2192    /// assert_eq!(sh.get_dominent_light_direction(), -Vec3::ONE.get_normalized());
2193    /// ```
2194    pub fn from_lights(lights: &[SHLight]) -> Self {
2195        unsafe { sh_create(lights.as_ptr(), lights.len() as i32) }
2196    }
2197
2198    /// Creates a SphericalHarmonic from an array of coefficients. Useful for loading stored data!
2199    /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/SphericalHarmonics.html>
2200    /// * `coefficients` - Must be an array with a length of 9!
2201    ///
2202    /// see also [`sh_create`]
2203    /// ### Examples
2204    /// ```
2205    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2206    /// use stereokit_rust::{maths::Vec3,
2207    ///                      util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2208    ///
2209    /// let mut sh0 = SphericalHarmonics::default();
2210    /// sh0.add([1.0, 0.0, 1.0], named_colors::RED);
2211    /// let coefficient = sh0.coefficients;
2212    ///
2213    /// let sh = SphericalHarmonics::new(coefficient);
2214    ///
2215    /// assert_eq!(sh.get_sample([1.0, 0.0, 1.0]), Color128 { r: 11.453729, g: 0.0, b: 0.0, a: 1.0 });
2216    /// assert_eq!(sh.get_dominent_light_direction(), Vec3::new(-1.0, 0.0, -1.0).get_normalized());
2217    /// ```
2218    pub fn new(coefficients: [Vec3; 9]) -> Self {
2219        SphericalHarmonics { coefficients }
2220    }
2221
2222    /// Adds a ‘directional light’ to the lighting approximation. This can be used to bake a multiple light setup, or
2223    /// accumulate light
2224    /// from a field of points.
2225    /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/Add.html>
2226    /// * `light_dir` - The direction of the light source.
2227    /// * `light_color` - Color of the light, in linear color space.
2228    ///
2229    /// see also [`sh_add`]
2230    /// ### Examples
2231    /// ```
2232    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2233    /// use stereokit_rust::{maths::Vec3,
2234    ///                      util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2235    ///
2236    /// let mut sh = SphericalHarmonics::default();
2237    /// sh.add([1.0, 0.0, 1.0], named_colors::RED)
2238    ///   .add([0.0, 1.0, 0.0], named_colors::GREEN)
2239    ///   .add([0.0, 0.0, 1.0], named_colors::BLUE);
2240    ///
2241    /// assert_eq!(sh.get_sample([1.0, 0.0, 1.0]), Color128 { r: 11.453729, g: -0.2956792, b: 4.4505944, a: 1.0 });
2242    /// assert_eq!(sh.get_dominent_light_direction(), Vec3 { x: -0.21951628, y: -0.21670417, z: -0.95123714 });
2243    ///```
2244    pub fn add(&mut self, light_dir: impl Into<Vec3>, light_color: impl Into<Color128>) -> &mut Self {
2245        let light_dir = light_dir.into();
2246        let color = light_color.into();
2247        unsafe { sh_add(self, light_dir, Vec3 { x: color.r, y: color.g, z: color.b }) };
2248        self
2249    }
2250
2251    /// Scales all the SphericalHarmonic’s coefficients! This behaves as if you’re modifying the brightness of the
2252    /// lighting this object represents.
2253    /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/Brightness.html>
2254    /// * `scale` - A multiplier for the coefficients! A value of 1 will leave everything the same, 0.5 will cut the
2255    ///   brightness in half, and a 2 will double the brightness.
2256    ///
2257    /// see also [`sh_brightness`]
2258    /// ### Examples
2259    /// ```
2260    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2261    /// use stereokit_rust::{maths::Vec3,
2262    ///                      util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2263    ///
2264    /// let mut sh = SphericalHarmonics::default();
2265    /// sh.add([1.0, 0.0, 1.0], named_colors::RED)
2266    ///   .brightness(0.5);
2267    ///
2268    /// assert_eq!(sh.get_sample([1.0, 0.0, 1.0]), Color128 { r: 5.726864, g: 0.0, b: 0.0, a: 1.0 });
2269    /// assert_eq!(sh.get_dominent_light_direction(), Vec3::new(-1.0, 0.0, -1.0).get_normalized());
2270    ///
2271    /// sh.brightness(2.0);
2272    /// assert_eq!(sh.get_sample([1.0, 0.0, 1.0]), Color128 { r: 11.453729, g: 0.0, b: 0.0, a: 1.0 });
2273    /// assert_eq!(sh.get_dominent_light_direction(), Vec3::new(-1.0, 0.0, -1.0).get_normalized());
2274    ///
2275    ///
2276    /// sh.brightness(0.0);
2277    /// assert_eq!(sh.get_sample([1.0, 0.0, 1.0]), Color128::BLACK);
2278    /// assert_eq!(sh.get_dominent_light_direction().x.is_nan(), true);
2279    /// ```
2280    pub fn brightness(&mut self, scale: f32) -> &mut Self {
2281        unsafe { sh_brightness(self, scale) };
2282        self
2283    }
2284
2285    /// Look up the color information in a particular direction!
2286    /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/Sample.html>
2287    /// * `normal` - The direction to look in. Should be normalized.
2288    ///
2289    /// Returns the color represented by the SH in the given direction.
2290    /// see also [`sh_brightness`]
2291    pub fn get_sample(&self, normal: impl Into<Vec3>) -> Color128 {
2292        unsafe { sh_lookup(self, normal.into()) }
2293    }
2294
2295    /// Returns the dominant direction of the light represented by this spherical harmonics data. The direction value is
2296    /// normalized.
2297    /// You can get the color of the light in this direction by using the struct’s Sample method:
2298    /// light.get_sample(-light.get_dominent_light_direction()).
2299    /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/DominantLightDirection.html>
2300    ///
2301    /// see also [`sh_brightness`]
2302    /// ### Examples
2303    /// ```
2304    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2305    /// use stereokit_rust::{maths::Vec3,
2306    ///                      util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2307    ///
2308    /// let mut sh = SphericalHarmonics::default();
2309    /// sh.add([1.0, 0.0, 1.0], named_colors::RED)
2310    ///   .add([1.0, 1.0, 0.0], named_colors::GREEN)
2311    ///   .add([0.0, 1.0, 1.0], named_colors::BLUE);
2312    ///
2313    /// assert_eq!(sh.get_dominent_light_direction(), Vec3 { x: -0.3088678, y: -0.6715365, z: -0.6735276 });
2314    /// ```
2315    pub fn get_dominent_light_direction(&self) -> Vec3 {
2316        unsafe { sh_dominant_dir(self) }
2317    }
2318
2319    /// Converts the SphericalHarmonic into a vector of coefficients 9 long. Useful for storing calculated data!
2320    /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/ToArray.html>
2321    ///
2322    /// Returns an array of coefficients 9 long.
2323    /// see also [`sh_brightness`]
2324    pub fn to_array(&self) -> [Vec3; 9] {
2325        self.coefficients
2326    }
2327}
2328
2329/// This class contains time information for the current session and frame!
2330/// <https://stereokit.net/Pages/StereoKit/Time.html>
2331///
2332/// ### Examples
2333/// ```
2334/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2335/// use stereokit_rust::util::Time;
2336///
2337/// // These are the expected results for tests on a PC:
2338/// assert_eq!(Time::get_totalf(), 0.0);
2339/// assert_eq!(Time::get_total(), 0.0);
2340/// assert_eq!(Time::get_stepf(), 0.0);
2341/// assert_eq!(Time::get_step(), 0.0);
2342///
2343/// // Time passes slowly:
2344/// Time::scale(0.5);
2345///
2346/// let mut total = 0.0f64;
2347/// let mut totalf = 0.0f32;
2348/// number_of_steps = 100;
2349/// test_steps!( // !!!! Get a proper main loop !!!!
2350///    if iter < number_of_steps + 2 {
2351///        assert_eq!(Time::get_frame(), iter + 1);
2352///
2353///        assert_eq!(Time::get_step_unscaled(), Time::get_step() * 2.0);
2354///
2355///        assert_eq!(Time::get_total(),          total + Time::get_step());
2356///        assert_eq!(Time::get_total_unscaled(), total * 2.0 + Time::get_step() * 2.0);
2357///
2358///        // precision is worse for f32
2359///        assert!((Time::get_totalf()          
2360///                 - totalf - Time::get_stepf()).abs() < 0.000001);
2361///        assert!((Time::get_total_unscaledf()
2362///                 - totalf * 2.0 - Time::get_stepf() * 2.0).abs() < 0.000001);
2363///    }
2364///    totalf = Time::get_totalf();
2365///    total = Time::get_total();
2366/// );
2367/// ```
2368pub struct Time;
2369
2370unsafe extern "C" {
2371    // Deprecated: pub fn time_get_raw() -> f64;
2372    // Deprecated: pub fn time_getf_unscaled() -> f32;
2373    // Deprecated: pub fn time_get_unscaled() -> f64;
2374    // Deprecated: pub fn time_getf() -> f32;
2375    // Deprecated: pub fn time_get() -> f64;
2376    // Deprecated: pub fn time_elapsedf_unscaled() -> f32;
2377    // Deprecated: pub fn time_elapsed_unscaled() -> f64;
2378    // Deprecated: pub fn time_elapsedf() -> f32;
2379    // Deprecated: pub fn time_elapsed() -> f64;
2380    pub fn time_total_raw() -> f64;
2381    pub fn time_totalf_unscaled() -> f32;
2382    pub fn time_total_unscaled() -> f64;
2383    pub fn time_totalf() -> f32;
2384    pub fn time_total() -> f64;
2385    pub fn time_stepf_unscaled() -> f32;
2386    pub fn time_step_unscaled() -> f64;
2387    pub fn time_stepf() -> f32;
2388    pub fn time_step() -> f64;
2389    pub fn time_scale(scale: f64);
2390    pub fn time_set_time(total_seconds: f64, frame_elapsed_seconds: f64);
2391    pub fn time_frame() -> u64;
2392}
2393
2394impl Time {
2395    /// Time is scaled by this value! Want time to pass slower? Set it to 0.5! Faster? Try 2!
2396    /// <https://stereokit.net/Pages/StereoKit/Time/Scale.html>
2397    ///
2398    /// see also [`time_scale`]
2399    /// ### Examples
2400    /// ```
2401    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2402    /// use stereokit_rust::util::Time;
2403    ///
2404    /// // Time passes faster:
2405    /// Time::scale(2.0);
2406    ///
2407    /// let mut total = 0.0f64;
2408    /// let mut totalf = 0.0f32;
2409    /// number_of_steps = 100;
2410    /// test_steps!( // !!!! Get a proper main loop !!!!
2411    ///     assert_eq!(Time::get_step_unscaled(), Time::get_step() / 2.0);
2412    /// );
2413    /// ```
2414    pub fn scale(factor: f64) {
2415        unsafe { time_scale(factor) }
2416    }
2417
2418    /// This allows you to override the application time! The application will progress from this time using the current
2419    /// timescale.
2420    /// <https://stereokit.net/Pages/StereoKit/Time/SetTime.html>
2421    /// * `total_seconds` - What time should it now be? The app will progress from this point in time.
2422    /// * `frame_elapsed_seconds` - How long was the previous frame? This is a number often used in motion calculations.
2423    ///   If left to zero, it’ll use the previous frame’s time, and if the previous frame’s time was also zero, it’ll
2424    ///   use 1/90.
2425    ///
2426    /// see also [`time_set_time`]
2427    /// ### Examples
2428    /// ```
2429    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2430    /// use stereokit_rust::util::Time;
2431    ///
2432    /// // Time passes faster:
2433    /// Time::set_time(10.0, 0.01);
2434    ///
2435    /// assert_eq!(Time::get_total(), 10.0);
2436    /// assert_eq!(Time::get_step(), 0.01);
2437    /// ```
2438    pub fn set_time(total_seconds: f64, frame_elapsed_seconds: f64) {
2439        unsafe { time_set_time(total_seconds, frame_elapsed_seconds) }
2440    }
2441
2442    /// The number of frames/steps since the app started.
2443    /// <https://stereokit.net/Pages/StereoKit/Time/Frame.html>
2444    ///
2445    /// see also [`time_frame`]
2446    /// ### Examples
2447    /// ```
2448    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2449    /// use stereokit_rust::util::Time;
2450    ///
2451    /// assert_eq!(Time::get_frame(), 0);
2452    ///
2453    /// test_steps!( // !!!! Get a proper main loop !!!!
2454    ///     if iter < number_of_steps + 2 {
2455    ///         assert_eq!(Time::get_frame(), iter + 1);
2456    ///     }
2457    /// );
2458    /// ```
2459    pub fn get_frame() -> u64 {
2460        unsafe { time_frame() }
2461    }
2462
2463    /// How many seconds have elapsed since the last frame? 64 bit time precision, calculated at the start of the frame.
2464    /// <https://stereokit.net/Pages/StereoKit/Time/Step.html>
2465    ///
2466    /// see also [`time_step`]
2467    /// ### Examples
2468    /// ```
2469    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2470    /// use stereokit_rust::util::Time;
2471    ///
2472    /// assert_eq!(Time::get_step(), 0.0);
2473    ///
2474    /// let mut total = 0.0f64;
2475    /// number_of_steps = 1000;
2476    /// test_steps!( // !!!! Get a proper main loop !!!!
2477    ///     if iter < number_of_steps + 2 {
2478    ///         assert_eq!(Time::get_total(), total + Time::get_step());
2479    ///     }
2480    ///     total = Time::get_total();
2481    /// );
2482    /// ```    
2483    pub fn get_step() -> f64 {
2484        unsafe { time_step() }
2485    }
2486
2487    /// How many seconds have elapsed since the last frame? 32 bit time precision, calculated at the start of the frame.
2488    /// <https://stereokit.net/Pages/StereoKit/Time/Stepf.html>
2489    ///
2490    /// see also [`time_stepf`]
2491    /// ### Examples
2492    /// ```
2493    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2494    /// use stereokit_rust::util::Time;
2495    ///
2496    /// assert_eq!(Time::get_stepf(), 0.0);
2497    ///
2498    /// let mut totalf = 0.0f32;
2499    /// number_of_steps = 1000;
2500    /// test_steps!( // !!!! Get a proper main loop !!!!
2501    ///     if iter < number_of_steps + 2 {
2502    ///         assert!((Time::get_totalf() - totalf - Time::get_stepf()).abs() < 0.000001);
2503    ///     }
2504    ///     totalf = Time::get_totalf();
2505    /// );
2506    /// ```
2507    pub fn get_stepf() -> f32 {
2508        unsafe { time_stepf() }
2509    }
2510
2511    /// How many seconds have elapsed since the last frame? 64 bit time precision, calculated at the start of the frame.
2512    /// This version is unaffected by the Time::scale value!
2513    /// <https://stereokit.net/Pages/StereoKit/Time/StepUnscaled.html>
2514    ///
2515    /// see also [`time_step_unscaled`]
2516    /// ### Examples
2517    /// ```
2518    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2519    /// use stereokit_rust::util::Time;
2520    ///
2521    /// assert_eq!(Time::get_step_unscaled(), 0.0);
2522    ///
2523    /// let mut total = 0.0f64;
2524    /// number_of_steps = 1000;
2525    /// test_steps!( // !!!! Get a proper main loop !!!!
2526    ///     if iter < number_of_steps + 2 {
2527    ///         assert_eq!(Time::get_total_unscaled(), total + Time::get_step_unscaled());
2528    ///     }
2529    ///     total = Time::get_total_unscaled();
2530    /// );
2531    /// ```
2532    pub fn get_step_unscaled() -> f64 {
2533        unsafe { time_step_unscaled() }
2534    }
2535
2536    /// How many seconds have elapsed since the last frame? 32 bit time precision, calculated at the start of the frame.
2537    /// This version is unaffected by the Time.Scale value!
2538    /// <https://stereokit.net/Pages/StereoKit/Time/StepUnscaledf.html>
2539    ///
2540    /// see also [`time_stepf_unscaled`]
2541    /// ### Examples
2542    /// ```
2543    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2544    /// use stereokit_rust::util::Time;
2545    ///
2546    /// assert_eq!(Time::get_step_unscaledf(), 0.0);
2547    ///
2548    /// let mut totalf = 0.0f32;
2549    /// number_of_steps = 1000;
2550    /// test_steps!( // !!!! Get a proper main loop !!!!
2551    ///     if iter < number_of_steps + 2 {
2552    ///         assert!((Time::get_total_unscaledf() - totalf - Time::get_step_unscaledf().abs() < 0.000001));
2553    ///     }
2554    ///     totalf = Time::get_total_unscaledf();
2555    /// );
2556    /// ```
2557    pub fn get_step_unscaledf() -> f32 {
2558        unsafe { time_stepf_unscaled() }
2559    }
2560
2561    /// How many seconds have elapsed since StereoKit was initialized? 64 bit time precision, calculated at the start of
2562    /// the frame.
2563    /// <https://stereokit.net/Pages/StereoKit/Time/Total.html>
2564    ///
2565    /// see also [`time_total`]
2566    /// ### Examples
2567    /// ```
2568    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2569    /// use stereokit_rust::util::Time;
2570    ///
2571    /// // Time passes faster:
2572    /// Time::scale(2.0);
2573    ///
2574    /// assert_eq!(Time::get_total(), 0.0);
2575    ///
2576    /// number_of_steps = 1000;
2577    /// test_steps!( // !!!! Get a proper main loop !!!!
2578    ///     if iter < number_of_steps + 2 {
2579    ///         assert_eq!(Time::get_total(), Time::get_total_unscaled() * 2.0);
2580    ///     }
2581    /// );
2582    /// ```
2583    pub fn get_total() -> f64 {
2584        unsafe { time_total() }
2585    }
2586
2587    /// How many seconds have elapsed since StereoKit was initialized? 32 bit time precision, calculated at the start of
2588    /// the frame.
2589    /// <https://stereokit.net/Pages/StereoKit/Time/Totalf.html>
2590    ///
2591    /// see also [`time_totalf`]
2592    /// ### Examples
2593    /// ```
2594    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2595    /// use stereokit_rust::util::Time;
2596    ///
2597    /// // Time passes faster:
2598    /// Time::scale(0.25);
2599    ///
2600    /// assert_eq!(Time::get_totalf(), 0.0);
2601    ///
2602    /// number_of_steps = 1000;
2603    /// test_steps!( // !!!! Get a proper main loop !!!!
2604    ///     if iter < number_of_steps + 2 {
2605    ///         assert_eq!(Time::get_totalf(), Time::get_total_unscaledf() / 4.0);
2606    ///     }
2607    /// );
2608    /// ```
2609    pub fn get_totalf() -> f32 {
2610        unsafe { time_totalf() }
2611    }
2612
2613    /// How many seconds have elapsed since StereoKit was initialized? 64 bit time precision, calculated at the start of
2614    /// the frame. This version is unaffected by the Time::scale value!
2615    /// <https://stereokit.net/Pages/StereoKit/Time/TotalUnscaled.html>
2616    ///
2617    /// see also [`time_total_unscaled`]
2618    /// see example in [`Time::get_total`]
2619    pub fn get_total_unscaled() -> f64 {
2620        unsafe { time_total_unscaled() }
2621    }
2622
2623    /// How many seconds have elapsed since StereoKit was initialized? 32 bit time precision, calculated at the start of
2624    /// the frame. This version is unaffected by the Time::scale value!
2625    /// <https://stereokit.net/Pages/StereoKit/Time/TotalUnscaledf.html>
2626    ///
2627    /// see also [`time_totalf_unscaled`]
2628    /// see example in [`Time::get_total_unscaled`]
2629    pub fn get_total_unscaledf() -> f32 {
2630        unsafe { time_totalf_unscaled() }
2631    }
2632}