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/// let display_type      = Device::get_display_type();
924/// let display_blend     = Device::get_display_blend();
925/// let device_tracking   = Device::get_tracking();
926/// let device_name       = Device::get_name().unwrap();
927/// let device_runtime    = Device::get_runtime().unwrap();
928/// let device_gpu        = Device::get_gpu().unwrap();
929/// let has_eye_gaze      = Device::has_eye_gaze();
930/// let has_hand_tracking = Device::has_hand_tracking();
931/// let valid_blend_none  = Device::valid_blend(DisplayBlend::None);
932/// let display_blend_none= Device::display_blend(DisplayBlend::None);
933///
934/// xr_mode_stop_here!();
935/// // These are the expected results for offscreen tests on a PC:
936/// assert_eq!(display_type, DisplayType::Flatscreen);
937/// assert_eq!(display_blend, DisplayBlend::Opaque);
938/// assert_eq!(device_tracking, DeviceTracking::None);
939/// assert_eq!(device_name, "Offscreen");
940/// assert_eq!(device_runtime, "None");
941///  assert_ne!(device_gpu, "Name of your GPU");
942/// assert_eq!(has_eye_gaze, false);
943/// assert_eq!(has_hand_tracking, false);
944/// assert_eq!(valid_blend_none, false);
945/// assert_eq!(display_blend_none, false);
946/// ```
947pub struct Device;
948
949unsafe extern "C" {
950    pub fn device_display_get_type() -> DisplayType;
951    pub fn device_display_get_blend() -> DisplayBlend;
952    pub fn device_display_set_blend(blend: DisplayBlend) -> Bool32T;
953    pub fn device_display_valid_blend(blend: DisplayBlend) -> Bool32T;
954    pub fn device_display_get_refresh_rate() -> f32;
955    pub fn device_display_get_width() -> i32;
956    pub fn device_display_get_height() -> i32;
957    pub fn device_display_get_fov() -> FovInfo;
958    pub fn device_get_tracking() -> DeviceTracking;
959    pub fn device_get_name() -> *const c_char;
960    pub fn device_get_runtime() -> *const c_char;
961    pub fn device_get_gpu() -> *const c_char;
962    pub fn device_has_eye_gaze() -> Bool32T;
963    pub fn device_has_hand_tracking() -> Bool32T;
964}
965
966impl Device {
967    /// Allows you to set and get the current blend mode of the device! Setting this may not succeed if the blend mode
968    /// is not valid.
969    /// <https://stereokit.net/Pages/StereoKit/Device/DisplayBlend.html>
970    ///
971    /// see also [`device_display_set_blend`]
972    /// ### Examples
973    /// ```
974    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
975    /// use stereokit_rust::{util::Device, sk::DisplayBlend};
976    ///
977    /// // These are the expected results for tests on a PC:
978    /// assert_eq!(Device::get_display_blend(), DisplayBlend::Opaque);
979    ///
980    /// xr_mode_stop_here!();
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    /// xr_mode_stop_here!();
1009    /// // These are the expected results for offscreen tests on a PC:
1010    /// assert_eq!(Device::get_display_type(), DisplayType::Flatscreen);
1011    /// ```
1012    pub fn get_display_type() -> DisplayType {
1013        unsafe { device_display_get_type() }
1014    }
1015
1016    /// This is the name of the OpenXR runtime that powers the current device! This can help you determine which
1017    /// implementation quirks to expect based on the codebase used. On the simulator, this will be "Simulator", and in
1018    /// other non-XR modes this will be "None".
1019    /// <https://stereokit.net/Pages/StereoKit/Device/Runtime.html>
1020    ///
1021    /// see also [`device_get_runtime`]
1022    /// ### Examples
1023    /// ```
1024    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1025    /// use stereokit_rust::util::Device;
1026    ///
1027    /// assert!(Device::get_runtime().is_ok());
1028    ///
1029    /// xr_mode_stop_here!();
1030    /// // These are the expected results for offscreen tests on a PC:
1031    /// assert_eq!(Device::get_runtime().unwrap(), "None");
1032    /// ```
1033    pub fn get_runtime<'a>() -> Result<&'a str, StereoKitError> {
1034        unsafe { CStr::from_ptr(device_get_runtime()) }
1035            .to_str()
1036            .map_err(|e| StereoKitError::CStrError(e.to_string()))
1037    }
1038
1039    /// The reported name of the GPU, this will differ between D3D and GL.
1040    /// <https://stereokit.net/Pages/StereoKit/Device/GPU.html>
1041    ///
1042    /// see also [`device_get_gpu`]
1043    pub fn get_gpu<'a>() -> Result<&'a str, StereoKitError> {
1044        unsafe { CStr::from_ptr(device_get_gpu()) }
1045            .to_str()
1046            .map_err(|e| StereoKitError::CStrError(e.to_string()))
1047    }
1048
1049    /// Does the device we’re on have eye tracking support present for input purposes? This is not an indicator that the
1050    /// user has given the application permission to access this information. See Input.Gaze for how to use this data.
1051    /// <https://stereokit.net/Pages/StereoKit/Device/HasEyeGaze.html>
1052    ///
1053    /// see also [`device_has_eye_gaze`]
1054    /// ### Examples
1055    /// ```
1056    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1057    /// use stereokit_rust::util::Device;
1058    ///
1059    /// let has_eye_gaze = Device::has_eye_gaze();
1060    ///
1061    /// xr_mode_stop_here!();
1062    /// // These are the expected results for offscreen tests on a PC:
1063    /// assert_eq!(has_eye_gaze, false);
1064    /// ```
1065    pub fn has_eye_gaze() -> bool {
1066        unsafe { device_has_eye_gaze() != 0 }
1067    }
1068
1069    /// Tells if the device is capable of tracking hands. This does not tell if the user is actually using their hands
1070    /// for input, merely that it’s possible to!
1071    /// <https://stereokit.net/Pages/StereoKit/Device/HasHandTracking.html>
1072    ///
1073    /// see also [`device_has_hand_tracking`]
1074    /// ### Examples
1075    /// ```
1076    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1077    /// use stereokit_rust::util::Device;
1078    ///
1079    /// let has_hand_tracking = Device::has_hand_tracking();
1080    ///
1081    /// xr_mode_stop_here!();
1082    /// // These are the expected results for offscreen tests on a PC:
1083    /// assert_eq!(has_hand_tracking, false);
1084    /// ```
1085    pub fn has_hand_tracking() -> bool {
1086        unsafe { device_has_hand_tracking() != 0 }
1087    }
1088
1089    /// This is the name of the active device! From OpenXR, this is the same as systemName from XrSystemProperties. The
1090    /// simulator will say “Simulator”.
1091    /// <https://stereokit.net/Pages/StereoKit/Device/Name.html>
1092    ///
1093    /// see also [`device_get_name`]
1094    /// ### Examples
1095    /// ```
1096    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1097    /// use stereokit_rust::util::Device;
1098    ///
1099    /// let name = Device::get_name();
1100    /// assert!(name.is_ok());
1101    ///
1102    /// xr_mode_stop_here!();
1103    /// // These are the expected results for offscreen tests on a PC:
1104    /// assert_eq!(Device::get_name().unwrap(), "Offscreen");
1105    /// ```
1106    pub fn get_name<'a>() -> Result<&'a str, StereoKitError> {
1107        unsafe { CStr::from_ptr(device_get_name()) }
1108            .to_str()
1109            .map_err(|e| StereoKitError::CStrError(e.to_string()))
1110    }
1111
1112    /// The tracking capabilities of this device! Is it 3DoF, rotation only? Or is it 6DoF, with positional tracking as
1113    /// well? Maybe it can’t track at all!
1114    /// <https://stereokit.net/Pages/StereoKit/Device/Tracking.html>
1115    ///
1116    /// see also [`device_get_tracking`]
1117    /// ### Examples
1118    /// ```
1119    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1120    /// use stereokit_rust::util::{Device, DeviceTracking};
1121    ///
1122    /// let tracking = Device::get_tracking();
1123    ///
1124    /// xr_mode_stop_here!();
1125    /// // These are the expected results for offscreen tests on a PC:
1126    /// assert_eq!(tracking, DeviceTracking::None);
1127    /// ```
1128    pub fn get_tracking() -> DeviceTracking {
1129        unsafe { device_get_tracking() }
1130    }
1131
1132    /// Tells if a particular blend mode is valid on this device. Some devices may be capable of more than one blend
1133    /// mode.
1134    /// <https://stereokit.net/Pages/StereoKit/Device/ValidBlend.html>
1135    ///
1136    /// see also [`device_display_valid_blend`]
1137    /// ### Examples
1138    /// ```
1139    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1140    /// use stereokit_rust::{util::Device, sk::DisplayBlend};
1141    ///
1142    /// assert_eq!(Device::valid_blend(DisplayBlend::Opaque), true);
1143    ///
1144    /// xr_mode_stop_here!();
1145    /// // These are the expected results for offscreen tests on a PC:
1146    /// assert_eq!(Device::valid_blend(DisplayBlend::None), false);
1147    /// assert_eq!(Device::valid_blend(DisplayBlend::Additive), false);
1148    /// assert_eq!(Device::valid_blend(DisplayBlend::Blend), false);
1149    /// assert_eq!(Device::valid_blend(DisplayBlend::AnyTransparent), false);
1150    /// ```
1151    pub fn valid_blend(blend: DisplayBlend) -> bool {
1152        unsafe { device_display_valid_blend(blend) != 0 }
1153    }
1154}
1155
1156/// A color/position pair for Gradient values!
1157/// <https://stereokit.net/Pages/StereoKit/GradientKey.html>
1158///
1159/// see [`Gradient`]
1160/// ### Examples
1161/// ```
1162/// use stereokit_rust::util::{GradientKey, Color128, named_colors};
1163///
1164/// let key0 = GradientKey::new(named_colors::GOLD, 0.75);
1165/// let key1 = GradientKey::new(   Color128::new(1.0, 0.84313726, 0.0, 1.0), 0.75);
1166/// let key2 = GradientKey{ color: Color128::new(1.0, 0.84313726, 0.0, 1.0), position: 0.75 };
1167///
1168/// assert_eq!(key0, key1);
1169/// assert_eq!(key1, key2);
1170/// ```
1171#[derive(Debug, Copy, Clone, PartialEq)]
1172#[repr(C)]
1173pub struct GradientKey {
1174    /// The color for this item, preferably in some form of linear color space. Gamma corrected colors will definitely
1175    /// not math correctly.
1176    pub color: Color128,
1177    /// Typically a value between 0-1! This is the position of the color along the ‘x-axis’ of the gradient.
1178    pub position: f32,
1179}
1180
1181impl GradientKey {
1182    /// A basic copy constructor for GradientKey.
1183    /// <https://stereokit.net/Pages/StereoKit/GradientKey/GradientKey.html>
1184    /// * `color_linear` - The color for this item, preferably in some form of linear color space. Gamma corrected
1185    ///   colors will definitely not math correctly.
1186    /// * `position` - Typically a value between 0-1! This is the position of the color along the ‘x-axis’ of the
1187    ///   gradient.
1188    pub fn new(color_linear: impl Into<Color128>, position: f32) -> Self {
1189        Self { color: color_linear.into(), position }
1190    }
1191}
1192
1193/// A Gradient is a sparse collection of color keys that are used to represent a ramp of colors! This class is largely
1194/// just storing colors and allowing you to sample between them.
1195///
1196/// Since the Gradient is just interpolating values, you can use whatever color space you want here, as long as it's
1197/// linear and not gamma! Gamma space RGB can't math properly at all. It can be RGB(linear), HSV, LAB, just remember
1198/// which one you have, and be sure to convert it appropriately later. Data is stored as float colors, so this'll be a
1199/// high accuracy blend!
1200/// <https://stereokit.net/Pages/StereoKit/Gradient.html>
1201///
1202/// see also [GradientKey] [`crate::tex::Tex::gen_particle`] [`crate::tex::SHCubemap::gen_cubemap_gradient`]
1203/// ### Examples
1204/// ```
1205/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1206/// use stereokit_rust::{maths::Vec3, system::AssetState, tex::{Tex, SHCubemap},
1207///                      util::{named_colors, Gradient, GradientKey, Color128}};
1208///
1209/// let keys = [
1210///     GradientKey::new(Color128::BLACK_TRANSPARENT, 0.0),
1211///     GradientKey::new(named_colors::RED, 0.1),
1212///     GradientKey::new(named_colors::CYAN, 0.4),
1213///     GradientKey::new(named_colors::BLUE, 0.5),
1214///     GradientKey::new(Color128::BLACK, 0.7)];
1215///
1216/// let sh_cubemap = SHCubemap::gen_cubemap_gradient(Gradient::new(Some(&keys)),
1217///                                                  Vec3::UP, 128);
1218/// sh_cubemap.render_as_sky();
1219///
1220/// let mut gradient = Gradient::new(None);
1221/// gradient
1222///     .add(Color128::BLACK_TRANSPARENT, 0.0)
1223///     .add(named_colors::YELLOW, 0.1)
1224///     .add(named_colors::LIGHT_BLUE, 0.4)
1225///     .add(named_colors::BLUE, 0.5)
1226///     .add(Color128::BLACK, 0.7);
1227/// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1228/// ```
1229pub struct Gradient(pub NonNull<_GradientT>);
1230
1231impl Drop for Gradient {
1232    fn drop(&mut self) {
1233        unsafe { gradient_destroy(self.0.as_ptr()) };
1234    }
1235}
1236
1237impl AsRef<Gradient> for Gradient {
1238    fn as_ref(&self) -> &Gradient {
1239        self
1240    }
1241}
1242
1243/// StereoKit internal type.
1244#[repr(C)]
1245#[derive(Debug)]
1246pub struct _GradientT {
1247    _unused: [u8; 0],
1248}
1249
1250/// StereoKit ffi type.
1251pub type GradientT = *mut _GradientT;
1252
1253unsafe extern "C" {
1254    pub fn gradient_create() -> GradientT;
1255    pub fn gradient_create_keys(in_arr_keys: *const GradientKey, count: i32) -> GradientT;
1256    pub fn gradient_add(gradient: GradientT, color_linear: Color128, position: f32);
1257    pub fn gradient_set(gradient: GradientT, index: i32, color_linear: Color128, position: f32);
1258    pub fn gradient_remove(gradient: GradientT, index: i32);
1259    pub fn gradient_count(gradient: GradientT) -> i32;
1260    pub fn gradient_get(gradient: GradientT, at: f32) -> Color128;
1261    pub fn gradient_get32(gradient: GradientT, at: f32) -> Color32;
1262    pub fn gradient_destroy(gradient: GradientT);
1263}
1264impl Gradient {
1265    /// Creates a new, completely empty gradient.
1266    /// <https://stereokit.net/Pages/StereoKit/Gradient/Gradient.html>
1267    /// * `keys` - These can be in any order that you like, they’ll be sorted by their GradientKey.position value
1268    ///   regardless!
1269    ///
1270    /// see also [`gradient_create`] [`gradient_create_keys`]
1271    /// ### Examples
1272    /// ```
1273    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1274    /// use stereokit_rust::{maths::Vec3, tex::{Tex, SHCubemap},
1275    ///                      util::{named_colors, Gradient, GradientKey, Color128}};
1276    ///
1277    /// let keys = [
1278    ///     GradientKey::new(Color128::BLACK_TRANSPARENT, 0.0),
1279    ///     GradientKey::new(named_colors::RED, 0.5),
1280    ///     GradientKey::new(Color128::BLACK, 0.7)];
1281    ///
1282    /// let gradient1 = Gradient::new(Some(&keys));
1283    /// assert_eq!(gradient1.get_count(), 3);
1284    /// let sh_cubemap = SHCubemap::gen_cubemap_gradient(gradient1, Vec3::UP, 16);
1285    /// sh_cubemap.render_as_sky();
1286    ///
1287    /// let mut gradient2 = Gradient::new(Some(&keys));
1288    /// gradient2.add(named_colors::CYAN, 0.4);
1289    /// assert_eq!(gradient2.get_count(), 4);
1290    /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient2));
1291    /// ```
1292    pub fn new(keys: Option<&[GradientKey]>) -> Self {
1293        match keys {
1294            Some(keys) => {
1295                Gradient(NonNull::new(unsafe { gradient_create_keys(keys.as_ptr(), keys.len() as i32) }).unwrap())
1296            }
1297            None => Gradient(NonNull::new(unsafe { gradient_create() }).unwrap()),
1298        }
1299    }
1300
1301    /// This adds a color key into the list. It’ll get inserted to the right slot based on its position.
1302    /// <https://stereokit.net/Pages/StereoKit/Gradient/Add.html>
1303    /// * `color_linear` - Any linear space color you like!
1304    /// * `position` - Typically a value between 0-1! This is the position of the color along the ‘x-axis’ of the
1305    ///   gradient.
1306    ///
1307    /// see also [`gradient_add`]
1308    /// ### Examples
1309    /// ```
1310    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1311    /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128}};
1312    ///
1313    /// let mut gradient = Gradient::new(None);
1314    /// gradient
1315    ///     .add(Color128::BLACK_TRANSPARENT, 0.0)
1316    ///     .add(named_colors::YELLOW, 0.1)
1317    ///     .add(named_colors::LIGHT_BLUE, 0.4)
1318    ///     .add(named_colors::BLUE, 0.5)
1319    ///     .add(Color128::BLACK, 0.7);
1320    /// assert_eq!(gradient.get_count(), 5);
1321    /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1322    /// ```
1323    pub fn add(&mut self, color_linear: impl Into<Color128>, position: f32) -> &mut Self {
1324        unsafe { gradient_add(self.0.as_ptr(), color_linear.into(), position) };
1325        self
1326    }
1327
1328    /// Updates the color key at the given index! This will NOT re-order color keys if they are moved past another
1329    /// key’s position, which could lead to strange behavior.
1330    /// <https://stereokit.net/Pages/StereoKit/Gradient/Set.html>
1331    /// * `index` - Index of the color key to change.
1332    /// * `color_linear` - Any linear space color you like!
1333    /// * `position` - Typically a value between 0-1! This is the position of the color along the ‘x-axis’ of the
1334    ///   gradient.
1335    ///
1336    /// see also [`gradient_set`]
1337    /// ### Examples
1338    /// ```
1339    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1340    /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient,  Color128}};
1341    ///
1342    /// let mut gradient = Gradient::new(None);
1343    /// gradient
1344    ///     .add(Color128::BLACK_TRANSPARENT, 0.0)
1345    ///     .add(named_colors::YELLOW, 0.1)
1346    ///     .add(named_colors::LIGHT_BLUE, 0.4)
1347    ///     .add(named_colors::BLUE, 0.5)
1348    ///     .add(Color128::BLACK, 0.7);
1349    /// assert_eq!(gradient.get_count(), 5);
1350    /// assert_eq!(gradient.get(0.3), Color128 { r: 0.7856209, g: 0.8980392, b: 0.6013072, a: 1.0 });
1351    ///
1352    /// gradient.set(2, named_colors::RED, 0.3);
1353    /// gradient.set(-20, named_colors::RED, -10.3); // out of bounds, should do nothing
1354    /// assert_eq!(gradient.get_count(), 5);
1355    /// assert_eq!(gradient.get(0.3), named_colors::RED.into());
1356    ///
1357    /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1358    /// ```
1359    pub fn set(&mut self, index: i32, color_linear: impl Into<Color128>, position: f32) -> &mut Self {
1360        if index < 0 || index >= self.get_count() {
1361            return self;
1362        }
1363        unsafe { gradient_set(self.0.as_ptr(), index, color_linear.into(), position) };
1364        self
1365    }
1366
1367    /// Removes the color key at the given index! This won't reindex the gradient so get_count will still return the
1368    /// same value.
1369    /// <https://stereokit.net/Pages/StereoKit/Gradient/Remove.html>
1370    /// * `index` - The index of the color key to remove.
1371    ///
1372    /// see also [`gradient_remove`]
1373    /// ### Examples
1374    /// ```
1375    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1376    /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128}};
1377    ///
1378    /// let mut gradient = Gradient::new(None);
1379    /// gradient
1380    ///     .add(Color128::BLACK_TRANSPARENT, 0.0)
1381    ///     .add(named_colors::YELLOW, 0.1)
1382    ///     .add(named_colors::LIGHT_BLUE, 0.4)
1383    ///     .add(named_colors::BLUE, 0.5)
1384    ///     .add(Color128::BLACK, 0.7);
1385    /// assert_eq!(gradient.get_count(), 5);
1386    /// assert_eq!(gradient.get(0.4), named_colors::LIGHT_BLUE.into());
1387    ///
1388    /// gradient.remove(2);
1389    /// gradient.remove(19).remove(-189);
1390    /// assert_eq!(gradient.get_count(), 5);
1391    /// assert_eq!(gradient.get(0.4), Color128 { r: 0.25, g: 0.25, b: 0.75, a: 1.0 });
1392    ///
1393    /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1394    /// ```
1395    pub fn remove(&mut self, index: i32) -> &mut Self {
1396        if index < 0 || index >= self.get_count() {
1397            return self;
1398        }
1399        unsafe { gradient_remove(self.0.as_ptr(), index) };
1400        self
1401    }
1402
1403    /// The number of color keys present in this gradient.
1404    /// <https://stereokit.net/Pages/StereoKit/Gradient/Count.html>
1405    ///
1406    /// see also [`gradient_count`]
1407    /// ### Examples
1408    /// ```
1409    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1410    /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128}};
1411    ///
1412    /// let mut gradient = Gradient::new(None);
1413    /// assert_eq!(gradient.get_count(), 0);
1414    ///
1415    /// gradient
1416    ///     .add(Color128::BLACK_TRANSPARENT, 0.0)
1417    ///     .add(named_colors::YELLOW, 0.1)
1418    ///     .add(Color128::BLACK, 0.7);
1419    /// assert_eq!(gradient.get_count(), 3);
1420    ///
1421    /// gradient.remove(1);
1422    /// assert_eq!(gradient.get_count(), 3);
1423    ///
1424    /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1425    ///```
1426    pub fn get_count(&self) -> i32 {
1427        unsafe { gradient_count(self.0.as_ptr()) }
1428    }
1429
1430    /// Samples the gradient’s color at the given position!
1431    /// <https://stereokit.net/Pages/StereoKit/Gradient/Get.html>
1432    /// * `at` - Typically a value between 0-1, but if you used larger or smaller values for your color key’s positions,
1433    ///   it’ll be in that range!
1434    ///
1435    /// Returns the interpolated color at the given position. If ‘at’ is smaller or larger than the gradient’s position
1436    /// range, then the color will be clamped to the color at the beginning or end of the gradient!
1437    /// see also [`gradient_get`]
1438    /// ### Examples
1439    /// ```
1440    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1441    /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128}};
1442    ///
1443    /// let mut gradient = Gradient::new(None);
1444    /// gradient
1445    ///     .add(Color128::BLACK_TRANSPARENT, 0.0)
1446    ///     .add(named_colors::YELLOW, 0.1)
1447    ///     .add(named_colors::LIGHT_BLUE, 0.4)
1448    ///     .add(named_colors::BLUE, 0.5)
1449    ///     .add(Color128::BLACK, 0.7);
1450    ///
1451    /// assert_eq!(gradient.get(0.3), Color128 { r: 0.7856209, g: 0.8980392, b: 0.6013072, a: 1.0 });
1452    /// assert_eq!(gradient.get(0.4), named_colors::LIGHT_BLUE.into());
1453    /// assert_eq!(gradient.get(0.5), named_colors::BLUE.into());
1454    ///
1455    /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1456    /// ```
1457    pub fn get(&self, at: f32) -> Color128 {
1458        unsafe { gradient_get(self.0.as_ptr(), at) }
1459    }
1460
1461    /// Samples the gradient’s color at the given position, and converts it to a 32 bit color. If your RGBA color
1462    /// values are outside of the 0-1 range, then you’ll get some issues as they’re converted to 0-255 range bytes!
1463    /// <https://stereokit.net/Pages/StereoKit/Gradient/Remove.html>
1464    /// * `at` - Typically a value between 0-1, but if you used larger or smaller values for your color key’s positions,
1465    ///   it’ll be in that range!
1466    ///
1467    /// Returns the interpolated 32 bit color at the given position. If ‘at’ is smaller or larger than the gradient’s
1468    /// position range, then the color will be clamped to the color at the beginning or end of the gradient!
1469    /// see also [`gradient_get32`]
1470    /// ### Examples
1471    /// ```
1472    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1473    /// use stereokit_rust::{tex::Tex, util::{named_colors, Gradient, Color128, Color32}};
1474    ///
1475    /// let mut gradient = Gradient::new(None);
1476    /// gradient
1477    ///     .add(Color128::BLACK_TRANSPARENT, 0.0)
1478    ///     .add(named_colors::YELLOW, 0.1)
1479    ///     .add(named_colors::LIGHT_BLUE, 0.4)
1480    ///     .add(named_colors::BLUE, 0.5)
1481    ///     .add(Color128::BLACK, 0.7);
1482    ///
1483    /// assert_eq!(gradient.get32(0.3), Color32 { r: 200, g: 229, b: 153, a: 255 });
1484    /// assert_eq!(gradient.get32(0.4), named_colors::LIGHT_BLUE);
1485    /// assert_eq!(gradient.get32(0.5), named_colors::BLUE);
1486    ///
1487    /// let tex_particule1 = Tex::gen_particle(128, 128, 0.2, Some(gradient));
1488    /// ```
1489    pub fn get32(&self, at: f32) -> Color32 {
1490        unsafe { gradient_get32(self.0.as_ptr(), at) }
1491    }
1492}
1493
1494/// file extension to filter ie ".txt" ".gltf"
1495/// <https://stereokit.net/Pages/StereoKit/Platform.html>
1496///
1497/// see also [`crate::system::Assets::MODEL_FORMATS`] [`crate::system::Assets::TEXTURE_FORMATS`]
1498#[derive(Debug, Default, Copy, Clone)]
1499#[repr(C)]
1500pub struct FileFilter {
1501    ext: [c_char; 32usize],
1502}
1503
1504impl FileFilter {
1505    pub fn new(str: impl AsRef<str>) -> Self {
1506        let mut value: [c_char; 32usize] = [0; 32usize];
1507        let c_array = CString::new(str.as_ref()).unwrap();
1508        let c_array = c_array.as_bytes_with_nul();
1509
1510        let mut c_array_iter = c_array.iter().map(|v| *v as c_char);
1511        value[0..c_array.len()].fill_with(|| c_array_iter.next().unwrap());
1512
1513        Self { ext: value }
1514    }
1515}
1516
1517/// This class contains some tools for hashing data within StereoKit! Certain systems in StereoKit use string hashes
1518/// instead of full strings for faster search and compare, like UI and Materials, so this class gives access to the code
1519/// SK uses for hashing. StereoKit currently internally uses a 64 bit FNV hash, though this detail should be pretty
1520/// transparent to developers
1521pub struct Hash;
1522
1523unsafe extern "C" {
1524    pub fn hash_string(str_utf8: *const c_char) -> IdHashT;
1525    pub fn hash_string_with(str_utf8: *const c_char, root: IdHashT) -> IdHashT;
1526    pub fn hash_int(val: i32) -> IdHashT;
1527    pub fn hash_int_with(val: i32, root: IdHashT) -> IdHashT;
1528}
1529
1530impl Hash {
1531    /// This will hash the UTF8 representation of the given string into a hash value that StereoKit can use.
1532    /// <https://stereokit.net/Pages/StereoKit/Hash/String.html>
1533    /// * `str` - A string (UTF8),that will be hashed.
1534    ///
1535    /// Returns a StereoKit hash representing the provided string.
1536    /// see also [`hash_string`] [`Hash::string_with`]
1537    /// ### Examples
1538    /// ```
1539    /// use stereokit_rust::util::Hash;
1540    ///
1541    /// let hash = Hash::string("Hello World");
1542    /// assert_eq!(hash, 4420528118743043111);
1543    /// ```
1544    pub fn string(str: impl AsRef<str>) -> IdHashT {
1545        let c_str = CString::new(str.as_ref()).unwrap();
1546        unsafe { hash_string(c_str.as_ptr()) }
1547    }
1548
1549    /// This will hash the UTF8 representation of the given string into a hash value that StereoKit can use. This
1550    /// overload allows you to combine your hash with an existing hash.
1551    /// <https://stereokit.net/Pages/StereoKit/Hash/String.html>
1552    /// * `str` - A string (UTF8),that will be hashed.
1553    /// * `root` - The hash value this new hash will start from.
1554    ///
1555    /// Returns a StereoKit hash representing a combination of the provided string and the root hash.
1556    /// see also [`hash_string_with`] [`Hash::string`]
1557    /// ### Examples
1558    /// ```
1559    /// use stereokit_rust::util::Hash;
1560    ///
1561    /// let hash1 = Hash::string("Hello");
1562    /// assert_eq!(hash1, 7201466553693376363);
1563    ///
1564    /// let hash2 = Hash::string_with(" World", hash1);
1565    /// assert_eq!(hash2, 4420528118743043111);
1566    /// ```
1567    pub fn string_with(str: impl AsRef<str>, root: IdHashT) -> IdHashT {
1568        let c_str = CString::new(str.as_ref()).unwrap();
1569        unsafe { hash_string_with(c_str.as_ptr(), root) }
1570    }
1571
1572    /// This will hash an integer into a hash value that StereoKit can use. This is helpful for adding in some
1573    /// uniqueness using something like a for loop index. This may be best when combined with additional hashes.
1574    /// <https://stereokit.net/Pages/StereoKit/Hash/Int.html>
1575    /// * `val` - An integer that will be hashed.
1576    ///
1577    /// Returns a StereoKit hash representing the provided integer.
1578    /// see also [`hash_int`] [`Hash::int_with`]
1579    /// ### Examples
1580    /// ```
1581    /// use stereokit_rust::util::Hash;
1582    ///
1583    /// let hash = Hash::int(123456789);
1584    /// assert_eq!(hash, 8379007418144316681);
1585    /// ```
1586    pub fn int(val: i32) -> IdHashT {
1587        unsafe { hash_int(val) }
1588    }
1589
1590    /// This will hash an integer into a hash value that StereoKit can use. This is helpful for adding in some
1591    /// uniqueness using something like a for loop index. This overload allows you to combine your hash with an existing
1592    /// hash.
1593    /// <https://stereokit.net/Pages/StereoKit/Hash/Int.html>
1594    /// * `val` - An integer that will be hashed.
1595    /// * `root` - The hash value this new hash will start from.
1596    ///
1597    /// Returns a StereoKit hash representing a combination of the provided string and the root hash.
1598    /// see also [`hash_int_with`] [`Hash::int`]
1599    /// ### Examples
1600    /// ```
1601    /// use stereokit_rust::util::Hash;
1602    ///
1603    /// let hash1 = Hash::int(123456789);
1604    /// assert_eq!(hash1, 8379007418144316681);
1605    ///
1606    /// let hash2 = Hash::int_with(123456789, hash1);
1607    /// assert_eq!(hash2, 13864748593440029765);
1608    /// ```
1609    pub fn int_with(int: i32, root: IdHashT) -> IdHashT {
1610        unsafe { hash_int_with(int, root) }
1611    }
1612}
1613
1614/// TODO: UNSTABLE: When opening the Platform.FilePicker, this enum describes how the picker should look and behave.
1615/// <https://stereokit.net/Pages/StereoKit/PickerMode.html>
1616#[derive(Debug, Copy, Clone, PartialEq, Eq)]
1617#[repr(u32)]
1618pub enum PickerMode {
1619    /// Allow opening a single file.
1620    Open = 0,
1621    /// Allow the user to enter or select the name of the destination file.
1622    Save = 1,
1623}
1624
1625/// This class provides some platform related code that runs cross-platform. You might be able to do many of these
1626/// things with rust or C#, but you might not be able to do them in as a portable manner as these methods do!
1627/// <https://stereokit.net/Pages/StereoKit/Platform.html>
1628///
1629/// ### Examples
1630/// ```
1631/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1632/// use stereokit_rust::{util::{Platform}, system::TextContext};
1633///
1634/// Platform::keyboard_show(true, TextContext::Text);
1635///
1636/// let mut path = std::env::current_dir().expect("Current directory should be readable");
1637/// path.push("assets/textures/");
1638/// assert!(path.is_dir());
1639/// path.push("readme.md");
1640/// assert!(path.is_file());
1641///
1642/// let file_content = Platform::read_file_text(&path)
1643///                                  .expect("File should be readable");
1644/// assert!(file_content.starts_with("# Images "));
1645///
1646/// assert!(Platform::write_file_text(path, file_content).is_ok());
1647/// ```
1648pub struct Platform;
1649
1650unsafe extern "C" {
1651    pub fn platform_file_picker(
1652        mode: PickerMode,
1653        callback_data: *mut c_void,
1654        picker_callback: ::std::option::Option<
1655            unsafe extern "C" fn(callback_data: *mut c_void, confirmed: Bool32T, filename: *const c_char),
1656        >,
1657        filters: *const FileFilter,
1658        filter_count: i32,
1659    );
1660    pub fn platform_file_picker_sz(
1661        mode: PickerMode,
1662        callback_data: *mut c_void,
1663        picker_callback_sz: ::std::option::Option<
1664            unsafe extern "C" fn(
1665                callback_data: *mut c_void,
1666                confirmed: Bool32T,
1667                filename_ptr: *const c_char,
1668                filename_length: i32,
1669            ),
1670        >,
1671        in_arr_filters: *const FileFilter,
1672        filter_count: i32,
1673    );
1674    pub fn platform_file_picker_close();
1675    pub fn platform_file_picker_visible() -> Bool32T;
1676    pub fn platform_read_file(
1677        filename_utf8: *const c_char,
1678        out_data: *mut *mut c_void,
1679        out_size: *mut usize,
1680    ) -> Bool32T;
1681    pub fn platform_write_file(filename_utf8: *const c_char, data: *mut c_void, size: usize) -> Bool32T;
1682    pub fn platform_write_file_text(filename_utf8: *const c_char, text_utf8: *const c_char) -> Bool32T;
1683    pub fn platform_keyboard_get_force_fallback() -> Bool32T;
1684    pub fn platform_keyboard_set_force_fallback(force_fallback: Bool32T);
1685    pub fn platform_keyboard_show(visible: Bool32T, type_: TextContext);
1686    pub fn platform_keyboard_visible() -> Bool32T;
1687    pub fn platform_keyboard_set_layout(
1688        type_: TextContext,
1689        keyboard_layout: *const *const c_char,
1690        layouts_num: i32,
1691    ) -> Bool32T;
1692}
1693
1694/// File_picker trampoline
1695///
1696/// see also [`Plaform::file_picker`]
1697unsafe extern "C" fn fp_trampoline<FS: FnMut(&str), FC: FnMut()>(
1698    user_data: *mut c_void,
1699    confirmed: Bool32T,
1700    filename: *const c_char,
1701) {
1702    let data = unsafe { &mut *(user_data as *mut (&mut FS, &mut FC)) };
1703    let (update, cancel) = data;
1704    if confirmed != 0 {
1705        let c_str = unsafe { CStr::from_ptr(filename).to_str().unwrap() };
1706        update(c_str)
1707    } else {
1708        cancel()
1709    }
1710}
1711
1712/// File_picker_sz trampoline
1713///
1714/// see also [`Plaform::file_picker`]
1715unsafe extern "C" fn fp_sz_trampoline<F: FnMut(bool, &str)>(
1716    user_data: *mut c_void,
1717    confirmed: Bool32T,
1718    filename: *const c_char,
1719    filename_length: i32,
1720) {
1721    let closure = unsafe { &mut *(user_data as *mut &mut F) };
1722    if confirmed != 0 && filename_length > 0 {
1723        let c_str = unsafe { CStr::from_ptr(filename).to_str().unwrap() };
1724        closure(true, c_str)
1725    } else {
1726        let c_str = "";
1727        closure(false, c_str)
1728    }
1729}
1730
1731impl Platform {
1732    /// Force the use of StereoKit’s built-in fallback keyboard instead of the system keyboard. This may be great for
1733    /// testing or look and feel matching, but the system keyboard should generally be preferred for accessibility
1734    /// reasons.
1735    /// <https://stereokit.net/Pages/StereoKit/Platform/ForceFallbackKeyboard.html>
1736    ///
1737    /// see also [`platform_keyboard_set_force_fallback`]
1738    /// ### Examples
1739    /// ```
1740    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1741    /// use stereokit_rust::util::Platform;
1742    ///
1743    /// assert_eq!(Platform::get_force_fallback_keyboard(), false);
1744    ///
1745    /// Platform::force_fallback_keyboard(true);
1746    /// assert_eq!(Platform::get_force_fallback_keyboard(), true);
1747    ///
1748    /// Platform::force_fallback_keyboard(false);
1749    /// assert_eq!(Platform::get_force_fallback_keyboard(), false);
1750    /// ```
1751    pub fn force_fallback_keyboard(force_fallback: bool) {
1752        unsafe { platform_keyboard_set_force_fallback(force_fallback as Bool32T) }
1753    }
1754
1755    /// TODO: UNSTABLE: Starts a file picker window! This will create a native file picker window if one is available in the current
1756    /// setup, and if it is not, it’ll create a fallback filepicker build using StereoKit’s UI.
1757    ///
1758    /// Flatscreen apps will show traditional file pickers, and UWP has an OS provided file picker that works in MR. All
1759    /// others currently use the fallback system.
1760    ///
1761    /// A note for UWP apps, UWP generally does not have permission to access random files, unless the user has chosen
1762    /// them with the picker! This picker properly handles permissions for individual files on UWP, but may have issues
1763    /// with files that reference other files, such as .gltf files with external textures. See [`Platform::write_file`]
1764    /// and [`Platform.read_file`] for manually reading and writing files in a cross-platfom manner.
1765    /// <https://stereokit.net/Pages/StereoKit/Platform/FilePicker.html>
1766    /// * `mode` - Are we trying to Open a file, or Save a file? This changes the appearance and behavior of the picker
1767    ///   to support the specified action.
1768    /// * `on_select_file` - This Action will be called with the proper filename when the picker has successfully
1769    ///   completed! On a cancel or close event, this Action is not called.
1770    /// * `on_cancel` - If the user cancels the file picker, or the picker is closed via FilePickerClose, this Action is
1771    ///   called.
1772    /// * `filters` - A list of file extensions that the picker should filter for. This is in the format of “.glb” and
1773    ///   is case insensitive.
1774    ///
1775    /// see also [`platform_file_picker`]
1776    pub fn file_picker<FS: FnMut(&str), FC: FnMut()>(
1777        mode: PickerMode,
1778        mut on_select_file: FS,
1779        mut on_cancel: FC,
1780        filters: &[impl AsRef<str>],
1781    ) {
1782        let mut c_filters = Vec::new();
1783        for filter in filters {
1784            c_filters.push(FileFilter::new(filter));
1785        }
1786
1787        let mut closure = (&mut on_select_file, &mut on_cancel);
1788        unsafe {
1789            platform_file_picker(
1790                mode,
1791                &mut closure as *mut _ as *mut c_void,
1792                Some(fp_trampoline::<FS, FC>),
1793                c_filters.as_slice().as_ptr(),
1794                c_filters.len() as i32,
1795            )
1796        }
1797    }
1798
1799    /// TODO: UNSTABLE: Starts a file picker window! This will create a native file picker window if one is available in the current
1800    /// setup, and if it is not, it’ll create a fallback filepicker build using StereoKit’s UI.
1801    ///
1802    /// Flatscreen apps will show traditional file pickers, and UWP has an OS provided file picker that works in MR. All
1803    /// others currently use the fallback system. Some pickers will block the system and return right away, but others
1804    /// will stick around and let users continue to interact with the app.
1805    ///
1806    /// A note for UWP apps, UWP generally does not have permission to access random files, unless the user has chosen
1807    /// them with the picker! This picker properly handles permissions for individual files on UWP, but may have issues
1808    /// with files that reference other files, such as .gltf files with external textures. See [`Platform::write_file`]
1809    /// and [`Platform.read_file`] for manually reading and writing files in a cross-platfom manner.
1810    /// <https://stereokit.net/Pages/StereoKit/Platform/FilePicker.html>
1811    /// * `mode` - Are we trying to Open a file, or Save a file? This changes the appearance and behavior of the picker
1812    ///   to support the specified action.
1813    /// * on_complete - This action will be called when the file picker has finished, either via a cancel event, or from
1814    ///   a confirm event. First parameter is a bool, where true indicates the presence of a valid filename, and false
1815    ///   indicates a failure or cancel event.
1816    /// * `filters` - A list of file extensions that the picker should filter for. This is in the format of “.glb” and
1817    ///   is case insensitive.
1818    ///
1819    /// see also [`platform_file_picker_sz`]
1820    pub fn file_picker_sz<F: FnMut(bool, &str)>(mode: PickerMode, mut on_complete: F, filters: &[impl AsRef<str>]) {
1821        let mut c_filters = Vec::new();
1822        for filter in filters {
1823            c_filters.push(FileFilter::new(filter));
1824        }
1825
1826        let mut closure = &mut on_complete;
1827        unsafe {
1828            platform_file_picker_sz(
1829                mode,
1830                &mut closure as *mut _ as *mut c_void,
1831                Some(fp_sz_trampoline::<F>),
1832                c_filters.as_slice().as_ptr(),
1833                c_filters.len() as i32,
1834            )
1835        }
1836    }
1837
1838    /// TODO: UNSTABLE: If the picker is visible, this will close it and immediately trigger a cancel event for the active picker.
1839    /// <https://stereokit.net/Pages/StereoKit/Platform/FilePickerClose.html>
1840    ///
1841    /// see also [`platform_file_picker_close`]
1842    pub fn file_picker_close() {
1843        unsafe { platform_file_picker_close() }
1844    }
1845
1846    /// Request or hide a soft keyboard for the user to type on. StereoKit will surface OS provided soft keyboards where
1847    /// available, and use a fallback keyboard when not. On systems with physical keyboards, soft keyboards generally
1848    /// will not be shown if the user has interacted with their physical keyboard recently.
1849    /// <https://stereokit.net/Pages/StereoKit/Platform/KeyboardShow.html>
1850    /// * `show` - Tells whether or not to open or close the soft keyboard.
1851    /// * `input_type` - Soft keyboards can change layout to optimize for the type of text that’s required. StereoKit
1852    ///   will request the soft keyboard layout that most closely represents the TextContext provided.
1853    ///
1854    /// see also [`platform_keyboard_show`]
1855    /// ### Examples
1856    /// ```
1857    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1858    /// use stereokit_rust::{util::Platform, system::TextContext};
1859    ///
1860    /// xr_mode_stop_here!();
1861    /// // On non XR mode, this will not show the keyboard, as it is assumed that the user has a physical keyboard.
1862    /// Platform::keyboard_show(true, TextContext::Text);
1863    /// assert_eq!(Platform::is_keyboard_visible(), false);
1864    ///
1865    /// Platform::keyboard_show(false, TextContext::Text);
1866    /// assert_eq!(Platform::is_keyboard_visible(), false);
1867    /// ```
1868    pub fn keyboard_show(show: bool, input_type: TextContext) {
1869        unsafe { platform_keyboard_show(show as Bool32T, input_type) }
1870    }
1871
1872    /// Replace the default keyboard type with a custom layout.
1873    /// <https://stereokit.net/Pages/StereoKit/Platform/KeyboardSetLayout.html>
1874    /// * `keyboard_type` - The type of keyboard to replace.
1875    /// * `keyboard_layouts` - Custom keyboard layout to replace the defualt layout.
1876    ///
1877    /// Returns `true` if keyboard type was swapped with the provided layout.
1878    /// see also [`platform_keyboard_set_layout`]
1879    /// ### Examples
1880    /// ```
1881    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1882    /// use stereokit_rust::{util::Platform, system::TextContext};
1883    ///
1884    /// pub const FR_KEY_TEXT: &str = r#"²|&|é|"|'|(|\-|è|_|ç|à|)|=|{|}|spr:sk/ui/backspace-\b-8-3|spr:sk/ui/close----close
1885    /// Tab-\t-9-3|a|z|e|r|t|y|u|i|o|p|^|$|[|]|\|
1886    /// Entrée-\n-13-4|q|s|d|f|g|h|j|k|l|m|ù|*|#|Entrée-\n-13-3
1887    /// 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
1888    /// 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|"#;
1889    ///
1890    /// 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
1891    /// Tab-\t-9-3|A|Z|E|R|T|Y|U|I|O|P|¨|£|Ê|É|È
1892    /// Entrée-\n-13-4|Q|S|D|F|G|H|J|K|L|M|%|µ|Ç|Entrée-\n-13-3
1893    /// 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
1894    /// 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|"#;
1895    ///
1896    /// pub const FR_KEY_TEXT_ALT: &str = r#"*|/|~|#|{|[|\||`|\\|^|@|]|}|æ|œ|spr:sk/ui/backspace-\b-8-3|spr:sk/ui/close----close
1897    /// Tab-\t-9-3|à|â|ä|ç|é|è|ê|ë|î|ï|ô|ö|«|»|¤
1898    /// Entrée-\n-13-4|ù|û|ü|ÿ|À|Â|Ä|Ç|É|È|Ê|Ë|%|Entrée-\n-13-3
1899    /// spr:sk/ui/shift--16-3-go_1|Î|Ï|Ô|Ö|Ù|Û|Ü|Ÿ|$|£|€|¥|✋|spr:sk/ui/shift--16-2-go_1|spr:sk/ui/arrow_up--38
1900    /// 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|"#;
1901    ///
1902    /// let keyboard_layouts = vec![FR_KEY_TEXT, FR_KEY_TEXT_SHIFT, FR_KEY_TEXT_ALT];
1903    ///
1904    /// assert_eq!(Platform::keyboard_set_layout(TextContext::Text, &keyboard_layouts), true);
1905    /// ```
1906    pub fn keyboard_set_layout(keyboard_type: TextContext, keyboard_layouts: &Vec<&str>) -> bool {
1907        let mut keyboard_layouts_c = vec![];
1908        for str in keyboard_layouts {
1909            let c_str = CString::new(*str).unwrap().into_raw() as *const c_char;
1910            keyboard_layouts_c.push(c_str);
1911        }
1912        unsafe {
1913            platform_keyboard_set_layout(
1914                keyboard_type,
1915                keyboard_layouts_c.as_slice().as_ptr(),
1916                keyboard_layouts_c.len() as i32,
1917            ) != 0
1918        }
1919    }
1920
1921    /// Reads the entire contents of the file as a UTF-8 string, taking advantage of any permissions that may have been
1922    /// granted by Platform::file_picker(_sz?). Returns Err on failure.
1923    /// <https://stereokit.net/Pages/StereoKit/Platform/ReadFileText.html>
1924    /// * `filename` - The path to the file to read.
1925    ///
1926    /// see also [`platform_read_file`]
1927    /// ### Examples
1928    /// ```
1929    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1930    /// use stereokit_rust::{util::{Platform}, system::TextContext};
1931    ///
1932    /// let mut path = std::env::current_dir().expect("Current directory should be readable");
1933    /// path.push("config.toml");
1934    /// assert!(path.is_file());
1935    ///
1936    /// let file_content = Platform::read_file_text(&path)
1937    ///                                  .expect("File should be readable");
1938    /// assert!(file_content.starts_with("[env]"));
1939    /// ```
1940    pub fn read_file_text<'a>(filename: impl AsRef<Path>) -> Result<&'a str, StereoKitError> {
1941        let path_buf = filename.as_ref().to_path_buf();
1942        let c_str = CString::new(
1943            path_buf
1944                .clone()
1945                .to_str() //
1946                .ok_or(StereoKitError::ReadFileError(path_buf.clone(), "Failed to convert path to string".into()))?,
1947        )?;
1948        let out_data = CString::new("H")?.into_raw() as *mut *mut c_void;
1949        let mut len = 0usize;
1950        let len_ptr: *mut usize = &mut len;
1951        if unsafe { platform_read_file(c_str.as_ptr(), out_data, len_ptr) != 0 } {
1952            unsafe { CStr::from_ptr(*out_data as *const c_char) }
1953                .to_str()
1954                .map_err(|e| StereoKitError::ReadFileError(path_buf.clone(), e.to_string()))
1955        } else {
1956            Err(StereoKitError::ReadFileError(path_buf, "Failed to read file".into()))
1957        }
1958    }
1959
1960    /// Reads the entire contents of the file as a byte array, taking advantage of any permissions that may have been
1961    /// granted by Platform.FilePicker.
1962    /// <https://stereokit.net/Pages/StereoKit/Platform/ReadFile.html>
1963    /// * `filename` - The path to the file to read.
1964    ///
1965    /// see also [`platform_read_file`]
1966    /// ### Examples
1967    /// ```
1968    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
1969    /// use stereokit_rust::{util::{Platform}, system::TextContext};
1970    ///
1971    /// let mut path = std::env::current_dir().expect("Current directory should be readable");
1972    /// path.push("assets/textures/");
1973    /// assert!(path.is_dir());
1974    /// path.push("readme.md");
1975    /// assert!(path.is_file());
1976    ///
1977    /// let file_content = Platform::read_file(&path)
1978    ///                                  .expect("File should be readable");
1979    /// assert!(file_content.starts_with(b"# Images "));
1980    /// ```
1981    pub fn read_file<'a>(filename: impl AsRef<Path>) -> Result<&'a [u8], StereoKitError> {
1982        let path_buf = filename.as_ref().to_path_buf();
1983        let c_str = CString::new(
1984            path_buf
1985                .clone()
1986                .to_str() //
1987                .ok_or(StereoKitError::ReadFileError(path_buf.clone(), "Failed to convert path to string".into()))?,
1988        )?;
1989        let out_data = CString::new("H")?.into_raw() as *mut *mut c_void;
1990        let mut len = 0usize;
1991        let len_ptr: *mut usize = &mut len;
1992        if unsafe { platform_read_file(c_str.as_ptr(), out_data, len_ptr) != 0 } {
1993            Ok(unsafe { std::slice::from_raw_parts(*out_data as *const u8, len) })
1994        } else {
1995            Err(StereoKitError::ReadFileError(path_buf, "Failed to read file".into()))
1996        }
1997    }
1998
1999    /// Writes a UTF-8 text file to the filesystem, taking advantage of any permissions that may have been granted by
2000    /// Platform::file_picker.
2001    /// <https://stereokit.net/Pages/StereoKit/Platform/WriteFile.html>
2002    /// * `filename` - The path to the file to write.
2003    /// * `text` -  A string to write to the file. This gets converted to a UTF-8 encoding.
2004    ///
2005    /// see also [`platform_write_file_text`]
2006    /// ### Examples
2007    /// ```
2008    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2009    /// use stereokit_rust::{util::{Platform}, system::TextContext};
2010    ///
2011    /// let mut path = std::env::current_dir().expect("Current directory should be readable");
2012    /// path.push("assets/icons/");
2013    /// assert!(path.is_dir());
2014    /// path.push("readme.md");
2015    /// assert!(path.is_file());
2016    ///
2017    /// let file_content = Platform::read_file_text(&path)
2018    ///                                  .expect("File should be readable");
2019    /// assert!(file_content.starts_with("# Images "));
2020    ///
2021    /// assert!(Platform::write_file_text(path, file_content).is_ok());
2022    /// ```
2023    pub fn write_file_text<S: AsRef<str>>(filename: impl AsRef<Path>, text: S) -> Result<bool, StereoKitError> {
2024        let path_buf = filename.as_ref().to_path_buf();
2025        let c_str = CString::new(
2026            path_buf
2027                .clone()
2028                .to_str() //
2029                .ok_or(StereoKitError::WriteFileError(path_buf.clone(), "Failed to convert path to string".into()))?,
2030        )?;
2031        let in_data = CString::new(text.as_ref())?.into_raw() as *const c_char;
2032        if unsafe { platform_write_file_text(c_str.as_ptr(), in_data) != 0 } {
2033            Ok(true)
2034        } else {
2035            Err(StereoKitError::WriteFileError(path_buf, "Failed to write file".into()))
2036        }
2037    }
2038
2039    /// Writes an array of bytes to the filesystem, taking advantage of any permissions that may have been granted by
2040    /// Platform::file_picker.
2041    /// <https://stereokit.net/Pages/StereoKit/Platform/WriteFile.html>
2042    /// * `filename` - The path to the file to write.
2043    /// * `data` - The data to write to the file.
2044    ///
2045    /// see also [`platform_write_file`]
2046    /// ### Examples
2047    /// ```
2048    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2049    /// use stereokit_rust::{util::{Platform}, system::TextContext};
2050    ///
2051    /// let mut path = std::env::current_dir().expect("Current directory should be readable");
2052    /// path.push("assets/icons/");
2053    /// assert!(path.is_dir());
2054    /// path.push("readme.md");
2055    /// assert!(path.is_file());
2056    ///
2057    /// let file_content = Platform::read_file(&path)
2058    ///                                  .expect("File should be readable");
2059    /// assert!(file_content.starts_with(b"# Images "));
2060    ///
2061    /// assert!(Platform::write_file(path, file_content).is_ok());
2062    /// ```
2063    pub fn write_file(filename: impl AsRef<Path>, data: &[u8]) -> Result<bool, StereoKitError> {
2064        let path_buf = filename.as_ref().to_path_buf();
2065        let c_str = CString::new(
2066            path_buf
2067                .clone()
2068                .to_str() //
2069                .ok_or(StereoKitError::WriteFileError(path_buf.clone(), "Failed to convert path to string".into()))?,
2070        )?;
2071        if unsafe { platform_write_file(c_str.as_ptr(), data.as_ptr() as *mut c_void, data.len()) != 0 } {
2072            Ok(true)
2073        } else {
2074            Err(StereoKitError::WriteFileError(path_buf, "Failed to write file".into()))
2075        }
2076    }
2077
2078    /// TODO: UNSTABLE: This will check if the file picker interface is currently visible. Some pickers will never show this, as they
2079    /// block the application until the picker has completed.
2080    /// <https://stereokit.net/Pages/StereoKit/Platform/FilePickerVisible.html>
2081    ///
2082    /// see also [`platform_file_picker_visible`]
2083    /// ### Examples
2084    /// ```
2085    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2086    /// use stereokit_rust::util::Platform;
2087    ///
2088    /// assert_eq!(Platform::get_file_picker_visible(), false);
2089    /// ```
2090    pub fn get_file_picker_visible() -> bool {
2091        unsafe { platform_file_picker_visible() != 0 }
2092    }
2093
2094    /// Force the use of StereoKit’s built-in fallback keyboard instead of the system keyboard. This may be great for
2095    /// testing or look and feel matching, but the system keyboard should generally be preferred for accessibility
2096    /// reasons.
2097    /// <https://stereokit.net/Pages/StereoKit/Platform/ForceFallbackKeyboard.html>
2098    ///
2099    /// see also [`platform_keyboard_get_force_fallback`]
2100    /// see example [`Platform::force_fallback_keyboard`]
2101    pub fn get_force_fallback_keyboard() -> bool {
2102        unsafe { platform_keyboard_get_force_fallback() != 0 }
2103    }
2104
2105    /// Check if a soft keyboard is currently visible. This may be an OS provided keyboard or StereoKit’s fallback
2106    /// keyboard, but will not indicate the presence of a physical keyboard.
2107    /// <https://stereokit.net/Pages/StereoKit/Platform/KeyboardVisible.html>
2108    ///
2109    /// see also [`platform_keyboard_visible`]
2110    /// see example [`Platform::keyboard_show`]
2111    pub fn is_keyboard_visible() -> bool {
2112        unsafe { platform_keyboard_visible() != 0 }
2113    }
2114}
2115
2116/// A light source used for creating SphericalHarmonics data.
2117/// <https://stereokit.net/Pages/StereoKit/SHLight.html>
2118///
2119/// see [`SphericalHarmonics`] see also [`crate::tex::SHCubemap`]
2120/// ### Examples
2121/// ```
2122/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2123/// use stereokit_rust::{maths::Vec3, tex::SHCubemap, util::{SHLight, named_colors, Color128}};
2124///
2125/// let light0 = SHLight::new([1.0, 0.2, 0.3], named_colors::RED);
2126/// let light1 = SHLight::new(Vec3::new(1.0, 0.2, 0.3), Color128::new(1.0, 0.0, 0.0, 1.0));
2127/// let light2 = SHLight{ dir_to: Vec3::new(1.0, 0.2, 0.3), color: named_colors::RED.into()};
2128///
2129/// assert_eq!(light0, light1);
2130/// assert_eq!(light1, light2);
2131/// ```
2132#[derive(Debug, Copy, Clone, PartialEq)]
2133#[repr(C)]
2134pub struct SHLight {
2135    /// Direction to the light source.
2136    pub dir_to: Vec3,
2137    /// Color of the light in linear space! Values here can exceed 1.
2138    pub color: Color128,
2139}
2140impl SHLight {
2141    /// A basic constructor for SHLight.
2142    /// <https://stereokit.net/Pages/StereoKit/SHLight.html>
2143    /// * `dir_to` - Direction to the light source.
2144    /// * `color` - Color of the light in linear space! Values here can exceed 1.
2145    pub fn new(dir_to: impl Into<Vec3>, color: impl Into<Color128>) -> Self {
2146        Self { dir_to: dir_to.into(), color: color.into() }
2147    }
2148}
2149/// Spherical Harmonics are kinda like Fourier, but on a sphere. That doesn’t mean terribly much to me, and could be
2150/// wrong, but check out here for more details about how Spherical Harmonics work in this context!
2151///
2152/// However, the more prctical thing is, SH can be a function that describes a value over the surface of a sphere! This
2153/// is particularly useful for lighting, since you can basically store the lighting information for a space in this
2154/// value! This is often used for lightmap data, or a light probe grid, but StereoKit just uses a single SH for the
2155/// entire scene. It’s a gross oversimplification, but looks quite good, and is really fast! That’s extremely great
2156/// when you’re trying to hit 60fps, or even 144fps.
2157/// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics.html>
2158///
2159/// see also: [`crate::tex::SHCubemap`]
2160/// ### Examples
2161/// ```
2162/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2163/// use stereokit_rust::{maths::Vec3,
2164///                      util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2165///
2166/// let light0 = SHLight::new([1.0, 0.0, 0.0], named_colors::RED);
2167/// let light1 = SHLight::new([0.0, 1.0, 0.0], named_colors::GREEN);
2168/// let light2 = SHLight::new([0.0, 0.0, 1.0], named_colors::BLUE);
2169///
2170/// let mut sh = SphericalHarmonics::from_lights(&[light0, light1, light2]);
2171/// sh.brightness(1.0)
2172///   .add(Vec3::NEG_Y, named_colors::GREEN)
2173///   .add(Vec3::NEG_Z, named_colors::BLUE)
2174///   .add(Vec3::NEG_X, named_colors::RED);
2175///
2176/// assert_eq!(sh.get_sample(Vec3::UP), Color128 { r: 0.5813507, g: 0.8046322, b: 0.5813487, a: 1.0 });
2177/// assert_eq!(sh.get_dominent_light_direction(), Vec3 { x: 0.27644092, y: 0.2728996, z: 0.9214696 });
2178/// ```
2179#[derive(Debug, Default, Copy, Clone, PartialEq)]
2180#[repr(C)]
2181pub struct SphericalHarmonics {
2182    pub coefficients: [Vec3; 9usize],
2183}
2184
2185unsafe extern "C" {
2186    pub fn sh_create(in_arr_lights: *const SHLight, light_count: i32) -> SphericalHarmonics;
2187    pub fn sh_brightness(ref_harmonics: *mut SphericalHarmonics, scale: f32);
2188    pub fn sh_add(ref_harmonics: *mut SphericalHarmonics, light_dir: Vec3, light_color: Vec3);
2189    pub fn sh_lookup(harmonics: *const SphericalHarmonics, normal: Vec3) -> Color128;
2190    pub fn sh_dominant_dir(harmonics: *const SphericalHarmonics) -> Vec3;
2191}
2192impl SphericalHarmonics {
2193    /// Creates a SphericalHarmonics approximation of the irradiance given from a set of directional lights!
2194    /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/FromLights.html>
2195    /// * `lights` - A list of directional lights!
2196    ///
2197    /// Returns a SphericalHarmonics approximation of the irradiance given from a set of directional lights!
2198    /// see also [`SHLight`]
2199    /// ### Examples
2200    /// ```
2201    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2202    /// use stereokit_rust::{maths::Vec3,
2203    ///                      util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2204    ///
2205    /// let light0 = SHLight::new([1.0, 1.0, 1.0], named_colors::RED);
2206    /// let light1 = SHLight::new([0.5, 0.5, 0.5], named_colors::RED);
2207    /// let light2 = SHLight::new([0.25, 0.25, 0.25], named_colors::RED);
2208    ///
2209    /// let sh = SphericalHarmonics::from_lights(&[light0, light1, light2]);
2210    ///
2211    /// assert_eq!(sh.get_sample(Vec3::UP), Color128 { r: 2.2098913, g: 0.0, b: 0.0, a: 1.0 });
2212    /// assert_eq!(sh.get_dominent_light_direction(), -Vec3::ONE.get_normalized());
2213    /// ```
2214    pub fn from_lights(lights: &[SHLight]) -> Self {
2215        unsafe { sh_create(lights.as_ptr(), lights.len() as i32) }
2216    }
2217
2218    /// Creates a SphericalHarmonic from an array of coefficients. Useful for loading stored data!
2219    /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/SphericalHarmonics.html>
2220    /// * `coefficients` - Must be an array with a length of 9!
2221    ///
2222    /// see also [`sh_create`]
2223    /// ### Examples
2224    /// ```
2225    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2226    /// use stereokit_rust::{maths::Vec3,
2227    ///                      util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2228    ///
2229    /// let mut sh0 = SphericalHarmonics::default();
2230    /// sh0.add([1.0, 0.0, 1.0], named_colors::RED);
2231    /// let coefficient = sh0.coefficients;
2232    ///
2233    /// let sh = SphericalHarmonics::new(coefficient);
2234    ///
2235    /// assert_eq!(sh.get_sample([1.0, 0.0, 1.0]), Color128 { r: 11.453729, g: 0.0, b: 0.0, a: 1.0 });
2236    /// assert_eq!(sh.get_dominent_light_direction(), Vec3::new(-1.0, 0.0, -1.0).get_normalized());
2237    /// ```
2238    pub fn new(coefficients: [Vec3; 9]) -> Self {
2239        SphericalHarmonics { coefficients }
2240    }
2241
2242    /// Adds a ‘directional light’ to the lighting approximation. This can be used to bake a multiple light setup, or
2243    /// accumulate light
2244    /// from a field of points.
2245    /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/Add.html>
2246    /// * `light_dir` - The direction of the light source.
2247    /// * `light_color` - Color of the light, in linear color space.
2248    ///
2249    /// see also [`sh_add`]
2250    /// ### Examples
2251    /// ```
2252    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2253    /// use stereokit_rust::{maths::Vec3,
2254    ///                      util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2255    ///
2256    /// let mut sh = SphericalHarmonics::default();
2257    /// sh.add([1.0, 0.0, 1.0], named_colors::RED)
2258    ///   .add([0.0, 1.0, 0.0], named_colors::GREEN)
2259    ///   .add([0.0, 0.0, 1.0], named_colors::BLUE);
2260    ///
2261    /// assert_eq!(sh.get_sample([1.0, 0.0, 1.0]), Color128 { r: 11.453729, g: -0.2956792, b: 4.4505944, a: 1.0 });
2262    /// assert_eq!(sh.get_dominent_light_direction(), Vec3 { x: -0.21951628, y: -0.21670417, z: -0.95123714 });
2263    ///```
2264    pub fn add(&mut self, light_dir: impl Into<Vec3>, light_color: impl Into<Color128>) -> &mut Self {
2265        let light_dir = light_dir.into();
2266        let color = light_color.into();
2267        unsafe { sh_add(self, light_dir, Vec3 { x: color.r, y: color.g, z: color.b }) };
2268        self
2269    }
2270
2271    /// Scales all the SphericalHarmonic’s coefficients! This behaves as if you’re modifying the brightness of the
2272    /// lighting this object represents.
2273    /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/Brightness.html>
2274    /// * `scale` - A multiplier for the coefficients! A value of 1 will leave everything the same, 0.5 will cut the
2275    ///   brightness in half, and a 2 will double the brightness.
2276    ///
2277    /// see also [`sh_brightness`]
2278    /// ### Examples
2279    /// ```
2280    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2281    /// use stereokit_rust::{maths::Vec3,
2282    ///                      util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2283    ///
2284    /// let mut sh = SphericalHarmonics::default();
2285    /// sh.add([1.0, 0.0, 1.0], named_colors::RED)
2286    ///   .brightness(0.5);
2287    ///
2288    /// assert_eq!(sh.get_sample([1.0, 0.0, 1.0]), Color128 { r: 5.726864, g: 0.0, b: 0.0, a: 1.0 });
2289    /// assert_eq!(sh.get_dominent_light_direction(), Vec3::new(-1.0, 0.0, -1.0).get_normalized());
2290    ///
2291    /// sh.brightness(2.0);
2292    /// assert_eq!(sh.get_sample([1.0, 0.0, 1.0]), Color128 { r: 11.453729, g: 0.0, b: 0.0, a: 1.0 });
2293    /// assert_eq!(sh.get_dominent_light_direction(), Vec3::new(-1.0, 0.0, -1.0).get_normalized());
2294    ///
2295    ///
2296    /// sh.brightness(0.0);
2297    /// assert_eq!(sh.get_sample([1.0, 0.0, 1.0]), Color128::BLACK);
2298    /// assert_eq!(sh.get_dominent_light_direction().x.is_nan(), true);
2299    /// ```
2300    pub fn brightness(&mut self, scale: f32) -> &mut Self {
2301        unsafe { sh_brightness(self, scale) };
2302        self
2303    }
2304
2305    /// Look up the color information in a particular direction!
2306    /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/Sample.html>
2307    /// * `normal` - The direction to look in. Should be normalized.
2308    ///
2309    /// Returns the color represented by the SH in the given direction.
2310    /// see also [`sh_brightness`]
2311    pub fn get_sample(&self, normal: impl Into<Vec3>) -> Color128 {
2312        unsafe { sh_lookup(self, normal.into()) }
2313    }
2314
2315    /// Returns the dominant direction of the light represented by this spherical harmonics data. The direction value is
2316    /// normalized.
2317    /// You can get the color of the light in this direction by using the struct’s Sample method:
2318    /// light.get_sample(-light.get_dominent_light_direction()).
2319    /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/DominantLightDirection.html>
2320    ///
2321    /// see also [`sh_brightness`]
2322    /// ### Examples
2323    /// ```
2324    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2325    /// use stereokit_rust::{maths::Vec3,
2326    ///                      util::{SHLight, named_colors, Color128, SphericalHarmonics}};
2327    ///
2328    /// let mut sh = SphericalHarmonics::default();
2329    /// sh.add([1.0, 0.0, 1.0], named_colors::RED)
2330    ///   .add([1.0, 1.0, 0.0], named_colors::GREEN)
2331    ///   .add([0.0, 1.0, 1.0], named_colors::BLUE);
2332    ///
2333    /// assert_eq!(sh.get_dominent_light_direction(), Vec3 { x: -0.3088678, y: -0.6715365, z: -0.6735276 });
2334    /// ```
2335    pub fn get_dominent_light_direction(&self) -> Vec3 {
2336        unsafe { sh_dominant_dir(self) }
2337    }
2338
2339    /// Converts the SphericalHarmonic into a vector of coefficients 9 long. Useful for storing calculated data!
2340    /// <https://stereokit.net/Pages/StereoKit/SphericalHarmonics/ToArray.html>
2341    ///
2342    /// Returns an array of coefficients 9 long.
2343    /// see also [`sh_brightness`]
2344    pub fn to_array(&self) -> [Vec3; 9] {
2345        self.coefficients
2346    }
2347}
2348
2349/// This class contains time information for the current session and frame!
2350/// <https://stereokit.net/Pages/StereoKit/Time.html>
2351///
2352/// ### Examples
2353/// ```
2354/// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2355/// use stereokit_rust::util::Time;
2356///
2357/// // These are the expected results for tests on a PC:
2358/// assert_eq!(Time::get_totalf(), 0.0);
2359/// assert_eq!(Time::get_total(), 0.0);
2360/// assert_eq!(Time::get_stepf(), 0.0);
2361/// assert_eq!(Time::get_step(), 0.0);
2362///
2363/// // Time passes slowly:
2364/// Time::scale(0.5);
2365///
2366/// let mut total = 0.0f64;
2367/// let mut totalf = 0.0f32;
2368/// number_of_steps = 100;
2369/// test_steps!( // !!!! Get a proper main loop !!!!
2370///    if iter < number_of_steps + 2 {
2371///        assert_eq!(Time::get_frame(), iter + 1);
2372///
2373///        assert_eq!(Time::get_step_unscaled(), Time::get_step() * 2.0);
2374///
2375///        assert_eq!(Time::get_total(),          total + Time::get_step());
2376///        assert_eq!(Time::get_total_unscaled(), total * 2.0 + Time::get_step() * 2.0);
2377///
2378///        // precision is worse for f32
2379///        assert!((Time::get_totalf()          
2380///                 - totalf - Time::get_stepf()).abs() < 0.000001);
2381///        assert!((Time::get_total_unscaledf()
2382///                 - totalf * 2.0 - Time::get_stepf() * 2.0).abs() < 0.000001);
2383///    }
2384///    totalf = Time::get_totalf();
2385///    total = Time::get_total();
2386/// );
2387/// ```
2388pub struct Time;
2389
2390unsafe extern "C" {
2391    // Deprecated: pub fn time_get_raw() -> f64;
2392    // Deprecated: pub fn time_getf_unscaled() -> f32;
2393    // Deprecated: pub fn time_get_unscaled() -> f64;
2394    // Deprecated: pub fn time_getf() -> f32;
2395    // Deprecated: pub fn time_get() -> f64;
2396    // Deprecated: pub fn time_elapsedf_unscaled() -> f32;
2397    // Deprecated: pub fn time_elapsed_unscaled() -> f64;
2398    // Deprecated: pub fn time_elapsedf() -> f32;
2399    // Deprecated: pub fn time_elapsed() -> f64;
2400    pub fn time_total_raw() -> f64;
2401    pub fn time_totalf_unscaled() -> f32;
2402    pub fn time_total_unscaled() -> f64;
2403    pub fn time_totalf() -> f32;
2404    pub fn time_total() -> f64;
2405    pub fn time_stepf_unscaled() -> f32;
2406    pub fn time_step_unscaled() -> f64;
2407    pub fn time_stepf() -> f32;
2408    pub fn time_step() -> f64;
2409    pub fn time_scale(scale: f64);
2410    pub fn time_set_time(total_seconds: f64, frame_elapsed_seconds: f64);
2411    pub fn time_frame() -> u64;
2412}
2413
2414impl Time {
2415    /// Time is scaled by this value! Want time to pass slower? Set it to 0.5! Faster? Try 2!
2416    /// <https://stereokit.net/Pages/StereoKit/Time/Scale.html>
2417    ///
2418    /// see also [`time_scale`]
2419    /// ### Examples
2420    /// ```
2421    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2422    /// use stereokit_rust::util::Time;
2423    ///
2424    /// // Time passes faster:
2425    /// Time::scale(2.0);
2426    ///
2427    /// let mut total = 0.0f64;
2428    /// let mut totalf = 0.0f32;
2429    /// number_of_steps = 100;
2430    /// test_steps!( // !!!! Get a proper main loop !!!!
2431    ///     assert_eq!(Time::get_step_unscaled(), Time::get_step() / 2.0);
2432    /// );
2433    /// ```
2434    pub fn scale(factor: f64) {
2435        unsafe { time_scale(factor) }
2436    }
2437
2438    /// This allows you to override the application time! The application will progress from this time using the current
2439    /// timescale.
2440    /// <https://stereokit.net/Pages/StereoKit/Time/SetTime.html>
2441    /// * `total_seconds` - What time should it now be? The app will progress from this point in time.
2442    /// * `frame_elapsed_seconds` - How long was the previous frame? This is a number often used in motion calculations.
2443    ///   If left to zero, it’ll use the previous frame’s time, and if the previous frame’s time was also zero, it’ll
2444    ///   use 1/90.
2445    ///
2446    /// see also [`time_set_time`]
2447    /// ### Examples
2448    /// ```
2449    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2450    /// use stereokit_rust::util::Time;
2451    ///
2452    /// // Time passes faster:
2453    /// Time::set_time(10.0, 0.01);
2454    ///
2455    /// assert_eq!(Time::get_total(), 10.0);
2456    /// assert_eq!(Time::get_step(), 0.01);
2457    /// ```
2458    pub fn set_time(total_seconds: f64, frame_elapsed_seconds: f64) {
2459        unsafe { time_set_time(total_seconds, frame_elapsed_seconds) }
2460    }
2461
2462    /// The number of frames/steps since the app started.
2463    /// <https://stereokit.net/Pages/StereoKit/Time/Frame.html>
2464    ///
2465    /// see also [`time_frame`]
2466    /// ### Examples
2467    /// ```
2468    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2469    /// use stereokit_rust::util::Time;
2470    ///
2471    /// assert_eq!(Time::get_frame(), 0);
2472    ///
2473    /// test_steps!( // !!!! Get a proper main loop !!!!
2474    ///     if iter < number_of_steps + 2 {
2475    ///         assert_eq!(Time::get_frame(), iter + 1);
2476    ///     }
2477    /// );
2478    /// ```
2479    pub fn get_frame() -> u64 {
2480        unsafe { time_frame() }
2481    }
2482
2483    /// How many seconds have elapsed since the last frame? 64 bit time precision, calculated at the start of the frame.
2484    /// <https://stereokit.net/Pages/StereoKit/Time/Step.html>
2485    ///
2486    /// see also [`time_step`]
2487    /// ### Examples
2488    /// ```
2489    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2490    /// use stereokit_rust::util::Time;
2491    ///
2492    /// assert_eq!(Time::get_step(), 0.0);
2493    ///
2494    /// let mut total = 0.0f64;
2495    /// number_of_steps = 100;
2496    /// test_steps!( // !!!! Get a proper main loop !!!!
2497    ///     if iter < number_of_steps + 2 {
2498    ///         assert_eq!(Time::get_total(), total + Time::get_step());
2499    ///     }
2500    ///     total = Time::get_total();
2501    /// );
2502    /// ```    
2503    pub fn get_step() -> f64 {
2504        unsafe { time_step() }
2505    }
2506
2507    /// How many seconds have elapsed since the last frame? 32 bit time precision, calculated at the start of the frame.
2508    /// <https://stereokit.net/Pages/StereoKit/Time/Stepf.html>
2509    ///
2510    /// see also [`time_stepf`]
2511    /// ### Examples
2512    /// ```
2513    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2514    /// use stereokit_rust::util::Time;
2515    ///
2516    /// assert_eq!(Time::get_stepf(), 0.0);
2517    ///
2518    /// let mut totalf = 0.0f32;
2519    /// number_of_steps = 100;
2520    /// test_steps!( // !!!! Get a proper main loop !!!!
2521    ///     if iter < number_of_steps + 2 {
2522    ///         assert!((Time::get_totalf() - totalf - Time::get_stepf()).abs() < 0.0001);
2523    ///     }
2524    ///     totalf = Time::get_totalf();
2525    /// );
2526    /// ```
2527    pub fn get_stepf() -> f32 {
2528        unsafe { time_stepf() }
2529    }
2530
2531    /// How many seconds have elapsed since the last frame? 64 bit time precision, calculated at the start of the frame.
2532    /// This version is unaffected by the Time::scale value!
2533    /// <https://stereokit.net/Pages/StereoKit/Time/StepUnscaled.html>
2534    ///
2535    /// see also [`time_step_unscaled`]
2536    /// ### Examples
2537    /// ```
2538    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2539    /// use stereokit_rust::util::Time;
2540    ///
2541    /// assert_eq!(Time::get_step_unscaled(), 0.0);
2542    ///
2543    /// let mut total = 0.0f64;
2544    /// number_of_steps = 100;
2545    /// test_steps!( // !!!! Get a proper main loop !!!!
2546    ///     if iter < number_of_steps + 2 {
2547    ///         assert_eq!(Time::get_total_unscaled(), total + Time::get_step_unscaled());
2548    ///     }
2549    ///     total = Time::get_total_unscaled();
2550    /// );
2551    /// ```
2552    pub fn get_step_unscaled() -> f64 {
2553        unsafe { time_step_unscaled() }
2554    }
2555
2556    /// How many seconds have elapsed since the last frame? 32 bit time precision, calculated at the start of the frame.
2557    /// This version is unaffected by the Time.Scale value!
2558    /// <https://stereokit.net/Pages/StereoKit/Time/StepUnscaledf.html>
2559    ///
2560    /// see also [`time_stepf_unscaled`]
2561    /// ### Examples
2562    /// ```
2563    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2564    /// use stereokit_rust::util::Time;
2565    ///
2566    /// assert_eq!(Time::get_step_unscaledf(), 0.0);
2567    ///
2568    /// let mut totalf = 0.0f32;
2569    /// number_of_steps = 100;
2570    /// test_steps!( // !!!! Get a proper main loop !!!!
2571    ///     if iter < number_of_steps + 2 {
2572    ///         assert!((Time::get_total_unscaledf() - totalf - Time::get_step_unscaledf().abs() < 0.0001));
2573    ///     }
2574    ///     totalf = Time::get_total_unscaledf();
2575    /// );
2576    /// ```
2577    pub fn get_step_unscaledf() -> f32 {
2578        unsafe { time_stepf_unscaled() }
2579    }
2580
2581    /// How many seconds have elapsed since StereoKit was initialized? 64 bit time precision, calculated at the start of
2582    /// the frame.
2583    /// <https://stereokit.net/Pages/StereoKit/Time/Total.html>
2584    ///
2585    /// see also [`time_total`]
2586    /// ### Examples
2587    /// ```
2588    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2589    /// use stereokit_rust::util::Time;
2590    ///
2591    /// // Time passes faster:
2592    /// Time::scale(2.0);
2593    ///
2594    /// assert_eq!(Time::get_total(), 0.0);
2595    ///
2596    /// number_of_steps = 100;
2597    /// test_steps!( // !!!! Get a proper main loop !!!!
2598    ///     if iter < number_of_steps + 2 {
2599    ///         assert_eq!(Time::get_total(), Time::get_total_unscaled() * 2.0);
2600    ///     }
2601    /// );
2602    /// ```
2603    pub fn get_total() -> f64 {
2604        unsafe { time_total() }
2605    }
2606
2607    /// How many seconds have elapsed since StereoKit was initialized? 32 bit time precision, calculated at the start of
2608    /// the frame.
2609    /// <https://stereokit.net/Pages/StereoKit/Time/Totalf.html>
2610    ///
2611    /// see also [`time_totalf`]
2612    /// ### Examples
2613    /// ```
2614    /// # stereokit_rust::test_init_sk!(); // !!!! Get a proper way to initialize sk !!!!
2615    /// use stereokit_rust::util::Time;
2616    ///
2617    /// // Time passes faster:
2618    /// Time::scale(0.25);
2619    ///
2620    /// assert_eq!(Time::get_totalf(), 0.0);
2621    ///
2622    /// number_of_steps = 100;
2623    /// test_steps!( // !!!! Get a proper main loop !!!!
2624    ///     if iter < number_of_steps + 2 {
2625    ///         assert_eq!(Time::get_totalf(), Time::get_total_unscaledf() / 4.0);
2626    ///     }
2627    /// );
2628    /// ```
2629    pub fn get_totalf() -> f32 {
2630        unsafe { time_totalf() }
2631    }
2632
2633    /// How many seconds have elapsed since StereoKit was initialized? 64 bit time precision, calculated at the start of
2634    /// the frame. This version is unaffected by the Time::scale value!
2635    /// <https://stereokit.net/Pages/StereoKit/Time/TotalUnscaled.html>
2636    ///
2637    /// see also [`time_total_unscaled`]
2638    /// see example in [`Time::get_total`]
2639    pub fn get_total_unscaled() -> f64 {
2640        unsafe { time_total_unscaled() }
2641    }
2642
2643    /// How many seconds have elapsed since StereoKit was initialized? 32 bit time precision, calculated at the start of
2644    /// the frame. This version is unaffected by the Time::scale value!
2645    /// <https://stereokit.net/Pages/StereoKit/Time/TotalUnscaledf.html>
2646    ///
2647    /// see also [`time_totalf_unscaled`]
2648    /// see example in [`Time::get_total_unscaled`]
2649    pub fn get_total_unscaledf() -> f32 {
2650        unsafe { time_totalf_unscaled() }
2651    }
2652}