Skip to main content

whiteout/
math.rs

1// SPDX-License-Identifier: BSD-3-Clause
2// Copyright (c) 2026 Fernando Sahmkow
3// AUTOGENERATED by tools/codegen/emit_rust.py — do not edit.
4// Regenerate via:  python -m tools.codegen.codegen <any> --backend rust-math
5
6//! Vector, quaternion and matrix types.
7//!
8//! Every type here is `#[repr(C)]` and `Copy`, laid out identically to its
9//! C++ counterpart, so it crosses the FFI boundary with no conversion and
10//! no allocation. Component-wise arithmetic, `dot` and `length` are plain
11//! Rust; anything with subtler semantics (Hamilton product, `slerp`,
12//! matrix inverse, the spline interpolators) calls into the C++
13//! implementation so this binding cannot drift from the library.
14
15#[repr(C)]
16#[derive(Clone, Copy, Debug, Default, PartialEq)]
17pub struct Vector2f {
18    pub x: f32,
19    pub y: f32,
20}
21
22const _: () = assert!(core::mem::size_of::<Vector2f>() == 8);
23
24#[repr(C)]
25#[derive(Clone, Copy, Debug, Default, PartialEq)]
26pub struct Vector3f {
27    pub x: f32,
28    pub y: f32,
29    pub z: f32,
30}
31
32const _: () = assert!(core::mem::size_of::<Vector3f>() == 12);
33
34#[repr(C)]
35#[derive(Clone, Copy, Debug, Default, PartialEq)]
36pub struct Vector4f {
37    pub x: f32,
38    pub y: f32,
39    pub z: f32,
40    pub w: f32,
41}
42
43const _: () = assert!(core::mem::size_of::<Vector4f>() == 16);
44
45#[repr(C)]
46#[derive(Clone, Copy, Debug, Default, PartialEq)]
47pub struct Quaternion {
48    pub x: f32,
49    pub y: f32,
50    pub z: f32,
51    pub w: f32,
52}
53
54const _: () = assert!(core::mem::size_of::<Quaternion>() == 16);
55
56#[repr(C)]
57#[derive(Clone, Copy, Debug, Default, PartialEq)]
58/// Row-major, matching the C++ `MatrixMethods` storage order.
59pub struct Matrix33f {
60    pub data: [[f32; 3]; 3],
61}
62
63const _: () = assert!(core::mem::size_of::<Matrix33f>() == 36);
64
65#[repr(C)]
66#[derive(Clone, Copy, Debug, Default, PartialEq)]
67/// Row-major, matching the C++ `MatrixMethods` storage order.
68pub struct Matrix44f {
69    pub data: [[f32; 4]; 4],
70}
71
72const _: () = assert!(core::mem::size_of::<Matrix44f>() == 64);
73
74// ── Vector2f ─────────────────────────────────────────────
75
76impl Vector2f {
77    #[inline]
78    pub const fn new(x: f32, y: f32) -> Self {
79        Self { x, y }
80    }
81
82    #[inline]
83    pub fn dot(self, other: Self) -> f32 {
84        self.x * other.x + self.y * other.y
85    }
86
87    #[inline]
88    pub fn length_squared(self) -> f32 {
89        self.dot(self)
90    }
91
92    #[inline]
93    pub fn length(self) -> f32 {
94        self.length_squared().sqrt()
95    }
96}
97
98impl core::ops::Add for Vector2f {
99    type Output = Self;
100    #[inline]
101    fn add(self, other: Self) -> Self {
102        Self {
103            x: self.x + other.x,
104            y: self.y + other.y,
105        }
106    }
107}
108
109impl core::ops::Sub for Vector2f {
110    type Output = Self;
111    #[inline]
112    fn sub(self, other: Self) -> Self {
113        Self {
114            x: self.x - other.x,
115            y: self.y - other.y,
116        }
117    }
118}
119
120impl core::ops::Mul for Vector2f {
121    type Output = Self;
122    #[inline]
123    fn mul(self, other: Self) -> Self {
124        Self {
125            x: self.x * other.x,
126            y: self.y * other.y,
127        }
128    }
129}
130
131impl core::ops::Div for Vector2f {
132    type Output = Self;
133    #[inline]
134    fn div(self, other: Self) -> Self {
135        Self {
136            x: self.x / other.x,
137            y: self.y / other.y,
138        }
139    }
140}
141
142impl core::ops::Mul<f32> for Vector2f {
143    type Output = Self;
144    #[inline]
145    fn mul(self, scalar: f32) -> Self {
146        Self {
147            x: self.x * scalar,
148            y: self.y * scalar,
149        }
150    }
151}
152
153impl core::ops::Div<f32> for Vector2f {
154    type Output = Self;
155    #[inline]
156    fn div(self, scalar: f32) -> Self {
157        let inv = 1.0f32 / scalar;
158        Self {
159            x: self.x * inv,
160            y: self.y * inv,
161        }
162    }
163}
164
165impl core::ops::Neg for Vector2f {
166    type Output = Self;
167    #[inline]
168    fn neg(self) -> Self {
169        Self {
170            x: -self.x,
171            y: -self.y,
172        }
173    }
174}
175
176impl Vector2f {
177    #[inline]
178    pub fn normalize(&mut self) {
179        unsafe { ffi::whiteout_v_Vector2f_normalize(self) }
180    }
181
182    #[inline]
183    pub fn normalized(self) -> Vector2f {
184        unsafe { ffi::whiteout_v_Vector2f_normalized(self) }
185    }
186
187    #[inline]
188    pub fn lerp(start: Vector2f, end: Vector2f, t: f32) -> Vector2f {
189        unsafe { ffi::whiteout_v_Vector2f_lerp(start, end, t) }
190    }
191
192    #[inline]
193    pub fn tcb_in_tangent(
194        prev: Vector2f,
195        current: Vector2f,
196        next: Vector2f,
197        tension: f32,
198        continuity: f32,
199        bias: f32,
200    ) -> Vector2f {
201        unsafe {
202            ffi::whiteout_v_Vector2f_tcb_in_tangent(prev, current, next, tension, continuity, bias)
203        }
204    }
205
206    #[inline]
207    pub fn tcb_out_tangent(
208        prev: Vector2f,
209        current: Vector2f,
210        next: Vector2f,
211        tension: f32,
212        continuity: f32,
213        bias: f32,
214    ) -> Vector2f {
215        unsafe {
216            ffi::whiteout_v_Vector2f_tcb_out_tangent(prev, current, next, tension, continuity, bias)
217        }
218    }
219
220    #[inline]
221    pub fn bezier_lerp(
222        start: Vector2f,
223        outtan: Vector2f,
224        intan: Vector2f,
225        end: Vector2f,
226        t: f32,
227    ) -> Vector2f {
228        unsafe { ffi::whiteout_v_Vector2f_bezier_lerp(start, outtan, intan, end, t) }
229    }
230
231    #[inline]
232    pub fn hermite_lerp(
233        prev: Vector2f,
234        start: Vector2f,
235        end: Vector2f,
236        next: Vector2f,
237        t: f32,
238    ) -> Vector2f {
239        unsafe { ffi::whiteout_v_Vector2f_hermite_lerp(prev, start, end, next, t) }
240    }
241}
242
243// ── Vector3f ─────────────────────────────────────────────
244
245impl Vector3f {
246    #[inline]
247    pub const fn new(x: f32, y: f32, z: f32) -> Self {
248        Self { x, y, z }
249    }
250
251    #[inline]
252    pub fn dot(self, other: Self) -> f32 {
253        self.x * other.x + self.y * other.y + self.z * other.z
254    }
255
256    #[inline]
257    pub fn length_squared(self) -> f32 {
258        self.dot(self)
259    }
260
261    #[inline]
262    pub fn length(self) -> f32 {
263        self.length_squared().sqrt()
264    }
265}
266
267impl core::ops::Add for Vector3f {
268    type Output = Self;
269    #[inline]
270    fn add(self, other: Self) -> Self {
271        Self {
272            x: self.x + other.x,
273            y: self.y + other.y,
274            z: self.z + other.z,
275        }
276    }
277}
278
279impl core::ops::Sub for Vector3f {
280    type Output = Self;
281    #[inline]
282    fn sub(self, other: Self) -> Self {
283        Self {
284            x: self.x - other.x,
285            y: self.y - other.y,
286            z: self.z - other.z,
287        }
288    }
289}
290
291impl core::ops::Mul for Vector3f {
292    type Output = Self;
293    #[inline]
294    fn mul(self, other: Self) -> Self {
295        Self {
296            x: self.x * other.x,
297            y: self.y * other.y,
298            z: self.z * other.z,
299        }
300    }
301}
302
303impl core::ops::Div for Vector3f {
304    type Output = Self;
305    #[inline]
306    fn div(self, other: Self) -> Self {
307        Self {
308            x: self.x / other.x,
309            y: self.y / other.y,
310            z: self.z / other.z,
311        }
312    }
313}
314
315impl core::ops::Mul<f32> for Vector3f {
316    type Output = Self;
317    #[inline]
318    fn mul(self, scalar: f32) -> Self {
319        Self {
320            x: self.x * scalar,
321            y: self.y * scalar,
322            z: self.z * scalar,
323        }
324    }
325}
326
327impl core::ops::Div<f32> for Vector3f {
328    type Output = Self;
329    #[inline]
330    fn div(self, scalar: f32) -> Self {
331        let inv = 1.0f32 / scalar;
332        Self {
333            x: self.x * inv,
334            y: self.y * inv,
335            z: self.z * inv,
336        }
337    }
338}
339
340impl core::ops::Neg for Vector3f {
341    type Output = Self;
342    #[inline]
343    fn neg(self) -> Self {
344        Self {
345            x: -self.x,
346            y: -self.y,
347            z: -self.z,
348        }
349    }
350}
351
352impl Vector3f {
353    #[inline]
354    pub fn normalize(&mut self) {
355        unsafe { ffi::whiteout_v_Vector3f_normalize(self) }
356    }
357
358    #[inline]
359    pub fn normalized(self) -> Vector3f {
360        unsafe { ffi::whiteout_v_Vector3f_normalized(self) }
361    }
362
363    #[inline]
364    pub fn lerp(start: Vector3f, end: Vector3f, t: f32) -> Vector3f {
365        unsafe { ffi::whiteout_v_Vector3f_lerp(start, end, t) }
366    }
367
368    #[inline]
369    pub fn tcb_in_tangent(
370        prev: Vector3f,
371        current: Vector3f,
372        next: Vector3f,
373        tension: f32,
374        continuity: f32,
375        bias: f32,
376    ) -> Vector3f {
377        unsafe {
378            ffi::whiteout_v_Vector3f_tcb_in_tangent(prev, current, next, tension, continuity, bias)
379        }
380    }
381
382    #[inline]
383    pub fn tcb_out_tangent(
384        prev: Vector3f,
385        current: Vector3f,
386        next: Vector3f,
387        tension: f32,
388        continuity: f32,
389        bias: f32,
390    ) -> Vector3f {
391        unsafe {
392            ffi::whiteout_v_Vector3f_tcb_out_tangent(prev, current, next, tension, continuity, bias)
393        }
394    }
395
396    #[inline]
397    pub fn bezier_lerp(
398        start: Vector3f,
399        outtan: Vector3f,
400        intan: Vector3f,
401        end: Vector3f,
402        t: f32,
403    ) -> Vector3f {
404        unsafe { ffi::whiteout_v_Vector3f_bezier_lerp(start, outtan, intan, end, t) }
405    }
406
407    #[inline]
408    pub fn hermite_lerp(
409        prev: Vector3f,
410        start: Vector3f,
411        end: Vector3f,
412        next: Vector3f,
413        t: f32,
414    ) -> Vector3f {
415        unsafe { ffi::whiteout_v_Vector3f_hermite_lerp(prev, start, end, next, t) }
416    }
417}
418
419// ── Vector4f ─────────────────────────────────────────────
420
421impl Vector4f {
422    #[inline]
423    pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
424        Self { x, y, z, w }
425    }
426
427    #[inline]
428    pub fn dot(self, other: Self) -> f32 {
429        self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w
430    }
431
432    #[inline]
433    pub fn length_squared(self) -> f32 {
434        self.dot(self)
435    }
436
437    #[inline]
438    pub fn length(self) -> f32 {
439        self.length_squared().sqrt()
440    }
441}
442
443impl core::ops::Add for Vector4f {
444    type Output = Self;
445    #[inline]
446    fn add(self, other: Self) -> Self {
447        Self {
448            x: self.x + other.x,
449            y: self.y + other.y,
450            z: self.z + other.z,
451            w: self.w + other.w,
452        }
453    }
454}
455
456impl core::ops::Sub for Vector4f {
457    type Output = Self;
458    #[inline]
459    fn sub(self, other: Self) -> Self {
460        Self {
461            x: self.x - other.x,
462            y: self.y - other.y,
463            z: self.z - other.z,
464            w: self.w - other.w,
465        }
466    }
467}
468
469impl core::ops::Mul for Vector4f {
470    type Output = Self;
471    #[inline]
472    fn mul(self, other: Self) -> Self {
473        Self {
474            x: self.x * other.x,
475            y: self.y * other.y,
476            z: self.z * other.z,
477            w: self.w * other.w,
478        }
479    }
480}
481
482impl core::ops::Div for Vector4f {
483    type Output = Self;
484    #[inline]
485    fn div(self, other: Self) -> Self {
486        Self {
487            x: self.x / other.x,
488            y: self.y / other.y,
489            z: self.z / other.z,
490            w: self.w / other.w,
491        }
492    }
493}
494
495impl core::ops::Mul<f32> for Vector4f {
496    type Output = Self;
497    #[inline]
498    fn mul(self, scalar: f32) -> Self {
499        Self {
500            x: self.x * scalar,
501            y: self.y * scalar,
502            z: self.z * scalar,
503            w: self.w * scalar,
504        }
505    }
506}
507
508impl core::ops::Div<f32> for Vector4f {
509    type Output = Self;
510    #[inline]
511    fn div(self, scalar: f32) -> Self {
512        let inv = 1.0f32 / scalar;
513        Self {
514            x: self.x * inv,
515            y: self.y * inv,
516            z: self.z * inv,
517            w: self.w * inv,
518        }
519    }
520}
521
522impl core::ops::Neg for Vector4f {
523    type Output = Self;
524    #[inline]
525    fn neg(self) -> Self {
526        Self {
527            x: -self.x,
528            y: -self.y,
529            z: -self.z,
530            w: -self.w,
531        }
532    }
533}
534
535impl Vector4f {
536    #[inline]
537    pub fn normalize(&mut self) {
538        unsafe { ffi::whiteout_v_Vector4f_normalize(self) }
539    }
540
541    #[inline]
542    pub fn normalized(self) -> Vector4f {
543        unsafe { ffi::whiteout_v_Vector4f_normalized(self) }
544    }
545
546    #[inline]
547    pub fn lerp(start: Vector4f, end: Vector4f, t: f32) -> Vector4f {
548        unsafe { ffi::whiteout_v_Vector4f_lerp(start, end, t) }
549    }
550
551    #[inline]
552    pub fn tcb_in_tangent(
553        prev: Vector4f,
554        current: Vector4f,
555        next: Vector4f,
556        tension: f32,
557        continuity: f32,
558        bias: f32,
559    ) -> Vector4f {
560        unsafe {
561            ffi::whiteout_v_Vector4f_tcb_in_tangent(prev, current, next, tension, continuity, bias)
562        }
563    }
564
565    #[inline]
566    pub fn tcb_out_tangent(
567        prev: Vector4f,
568        current: Vector4f,
569        next: Vector4f,
570        tension: f32,
571        continuity: f32,
572        bias: f32,
573    ) -> Vector4f {
574        unsafe {
575            ffi::whiteout_v_Vector4f_tcb_out_tangent(prev, current, next, tension, continuity, bias)
576        }
577    }
578
579    #[inline]
580    pub fn bezier_lerp(
581        start: Vector4f,
582        outtan: Vector4f,
583        intan: Vector4f,
584        end: Vector4f,
585        t: f32,
586    ) -> Vector4f {
587        unsafe { ffi::whiteout_v_Vector4f_bezier_lerp(start, outtan, intan, end, t) }
588    }
589
590    #[inline]
591    pub fn hermite_lerp(
592        prev: Vector4f,
593        start: Vector4f,
594        end: Vector4f,
595        next: Vector4f,
596        t: f32,
597    ) -> Vector4f {
598        unsafe { ffi::whiteout_v_Vector4f_hermite_lerp(prev, start, end, next, t) }
599    }
600}
601
602// ── Quaternion ─────────────────────────────────────────────
603
604impl Quaternion {
605    #[inline]
606    pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
607        Self { x, y, z, w }
608    }
609
610    #[inline]
611    pub fn dot(self, other: Self) -> f32 {
612        self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w
613    }
614
615    #[inline]
616    pub fn length_squared(self) -> f32 {
617        self.dot(self)
618    }
619
620    #[inline]
621    pub fn length(self) -> f32 {
622        self.length_squared().sqrt()
623    }
624}
625
626impl core::ops::Add for Quaternion {
627    type Output = Self;
628    #[inline]
629    fn add(self, other: Self) -> Self {
630        Self {
631            x: self.x + other.x,
632            y: self.y + other.y,
633            z: self.z + other.z,
634            w: self.w + other.w,
635        }
636    }
637}
638
639impl core::ops::Sub for Quaternion {
640    type Output = Self;
641    #[inline]
642    fn sub(self, other: Self) -> Self {
643        Self {
644            x: self.x - other.x,
645            y: self.y - other.y,
646            z: self.z - other.z,
647            w: self.w - other.w,
648        }
649    }
650}
651
652impl core::ops::Mul<f32> for Quaternion {
653    type Output = Self;
654    #[inline]
655    fn mul(self, scalar: f32) -> Self {
656        Self {
657            x: self.x * scalar,
658            y: self.y * scalar,
659            z: self.z * scalar,
660            w: self.w * scalar,
661        }
662    }
663}
664
665impl core::ops::Div<f32> for Quaternion {
666    type Output = Self;
667    #[inline]
668    fn div(self, scalar: f32) -> Self {
669        let inv = 1.0f32 / scalar;
670        Self {
671            x: self.x * inv,
672            y: self.y * inv,
673            z: self.z * inv,
674            w: self.w * inv,
675        }
676    }
677}
678
679impl core::ops::Neg for Quaternion {
680    type Output = Self;
681    #[inline]
682    fn neg(self) -> Self {
683        Self {
684            x: -self.x,
685            y: -self.y,
686            z: -self.z,
687            w: -self.w,
688        }
689    }
690}
691
692impl core::ops::Mul for Quaternion {
693    type Output = Quaternion;
694    #[inline]
695    fn mul(self, other: Quaternion) -> Quaternion {
696        unsafe { ffi::whiteout_v_Quaternion_mul(self, other) }
697    }
698}
699
700impl Quaternion {
701    #[inline]
702    pub fn normalize(&mut self) {
703        unsafe { ffi::whiteout_v_Quaternion_normalize(self) }
704    }
705
706    #[inline]
707    pub fn normalized(self) -> Quaternion {
708        unsafe { ffi::whiteout_v_Quaternion_normalized(self) }
709    }
710
711    #[inline]
712    pub fn conjugate(self) -> Quaternion {
713        unsafe { ffi::whiteout_v_Quaternion_conjugate(self) }
714    }
715
716    #[inline]
717    pub fn inverse(self) -> Quaternion {
718        unsafe { ffi::whiteout_v_Quaternion_inverse(self) }
719    }
720
721    #[inline]
722    pub fn log(self) -> Quaternion {
723        unsafe { ffi::whiteout_v_Quaternion_log(self) }
724    }
725
726    #[inline]
727    pub fn exp(self) -> Quaternion {
728        unsafe { ffi::whiteout_v_Quaternion_exp(self) }
729    }
730
731    #[inline]
732    pub fn to_euler_angles(self) -> Vector3f {
733        unsafe { ffi::whiteout_v_Quaternion_to_euler_angles(self) }
734    }
735
736    #[inline]
737    pub fn rotate_vector(self, v: Vector3f) -> Vector3f {
738        unsafe { ffi::whiteout_v_Quaternion_rotate_vector(self, v) }
739    }
740
741    #[inline]
742    pub fn identity() -> Quaternion {
743        unsafe { ffi::whiteout_v_Quaternion_identity() }
744    }
745
746    #[inline]
747    pub fn from_axis_angle(axis: Vector3f, angle_rad: f32) -> Quaternion {
748        unsafe { ffi::whiteout_v_Quaternion_from_axis_angle(axis, angle_rad) }
749    }
750
751    #[inline]
752    pub fn from_euler_angles(euler_rad: Vector3f) -> Quaternion {
753        unsafe { ffi::whiteout_v_Quaternion_from_euler_angles(euler_rad) }
754    }
755
756    #[inline]
757    pub fn slerp(a: Quaternion, b: Quaternion, t: f32) -> Quaternion {
758        unsafe { ffi::whiteout_v_Quaternion_slerp(a, b, t) }
759    }
760
761    #[inline]
762    pub fn squad(
763        start: Quaternion,
764        outtan: Quaternion,
765        inttan: Quaternion,
766        end: Quaternion,
767        t: f32,
768    ) -> Quaternion {
769        unsafe { ffi::whiteout_v_Quaternion_squad(start, outtan, inttan, end, t) }
770    }
771
772    #[inline]
773    pub fn ln_dif(a: Quaternion, b: Quaternion) -> Quaternion {
774        unsafe { ffi::whiteout_v_Quaternion_ln_dif(a, b) }
775    }
776}
777
778// ── Matrix33f ─────────────────────────────────────────────
779
780impl Matrix33f {
781    pub const DIM: usize = 3;
782
783    #[inline]
784    pub const fn from_rows(data: [[f32; 3]; 3]) -> Self {
785        Self { data }
786    }
787}
788
789impl core::ops::Index<(usize, usize)> for Matrix33f {
790    type Output = f32;
791    #[inline]
792    fn index(&self, (row, col): (usize, usize)) -> &f32 {
793        &self.data[row][col]
794    }
795}
796
797impl core::ops::IndexMut<(usize, usize)> for Matrix33f {
798    #[inline]
799    fn index_mut(&mut self, (row, col): (usize, usize)) -> &mut f32 {
800        &mut self.data[row][col]
801    }
802}
803
804impl core::ops::Add for Matrix33f {
805    type Output = Matrix33f;
806    #[inline]
807    fn add(self, other: Matrix33f) -> Matrix33f {
808        unsafe { ffi::whiteout_v_Matrix33f_add(self, other) }
809    }
810}
811
812impl core::ops::Sub for Matrix33f {
813    type Output = Matrix33f;
814    #[inline]
815    fn sub(self, other: Matrix33f) -> Matrix33f {
816        unsafe { ffi::whiteout_v_Matrix33f_sub(self, other) }
817    }
818}
819
820impl core::ops::Mul for Matrix33f {
821    type Output = Matrix33f;
822    #[inline]
823    fn mul(self, other: Matrix33f) -> Matrix33f {
824        unsafe { ffi::whiteout_v_Matrix33f_mul(self, other) }
825    }
826}
827
828impl core::ops::Mul<f32> for Matrix33f {
829    type Output = Matrix33f;
830    #[inline]
831    fn mul(self, scalar: f32) -> Matrix33f {
832        unsafe { ffi::whiteout_v_Matrix33f_mul_scalar(self, scalar) }
833    }
834}
835
836impl Matrix33f {
837    #[inline]
838    pub fn identity() -> Matrix33f {
839        unsafe { ffi::whiteout_v_Matrix33f_identity() }
840    }
841
842    #[inline]
843    pub fn zero() -> Matrix33f {
844        unsafe { ffi::whiteout_v_Matrix33f_zero() }
845    }
846}
847
848// ── Matrix44f ─────────────────────────────────────────────
849
850impl Matrix44f {
851    pub const DIM: usize = 4;
852
853    #[inline]
854    pub const fn from_rows(data: [[f32; 4]; 4]) -> Self {
855        Self { data }
856    }
857}
858
859impl core::ops::Index<(usize, usize)> for Matrix44f {
860    type Output = f32;
861    #[inline]
862    fn index(&self, (row, col): (usize, usize)) -> &f32 {
863        &self.data[row][col]
864    }
865}
866
867impl core::ops::IndexMut<(usize, usize)> for Matrix44f {
868    #[inline]
869    fn index_mut(&mut self, (row, col): (usize, usize)) -> &mut f32 {
870        &mut self.data[row][col]
871    }
872}
873
874impl core::ops::Add for Matrix44f {
875    type Output = Matrix44f;
876    #[inline]
877    fn add(self, other: Matrix44f) -> Matrix44f {
878        unsafe { ffi::whiteout_v_Matrix44f_add(self, other) }
879    }
880}
881
882impl core::ops::Sub for Matrix44f {
883    type Output = Matrix44f;
884    #[inline]
885    fn sub(self, other: Matrix44f) -> Matrix44f {
886        unsafe { ffi::whiteout_v_Matrix44f_sub(self, other) }
887    }
888}
889
890impl core::ops::Mul for Matrix44f {
891    type Output = Matrix44f;
892    #[inline]
893    fn mul(self, other: Matrix44f) -> Matrix44f {
894        unsafe { ffi::whiteout_v_Matrix44f_mul(self, other) }
895    }
896}
897
898impl core::ops::Mul<f32> for Matrix44f {
899    type Output = Matrix44f;
900    #[inline]
901    fn mul(self, scalar: f32) -> Matrix44f {
902        unsafe { ffi::whiteout_v_Matrix44f_mul_scalar(self, scalar) }
903    }
904}
905
906impl Matrix44f {
907    #[inline]
908    pub fn identity() -> Matrix44f {
909        unsafe { ffi::whiteout_v_Matrix44f_identity() }
910    }
911
912    #[inline]
913    pub fn zero() -> Matrix44f {
914        unsafe { ffi::whiteout_v_Matrix44f_zero() }
915    }
916
917    #[inline]
918    pub fn translation(t: Vector3f) -> Matrix44f {
919        unsafe { ffi::whiteout_v_Matrix44f_translation(t) }
920    }
921
922    #[inline]
923    pub fn rotation(q: Quaternion) -> Matrix44f {
924        unsafe { ffi::whiteout_v_Matrix44f_rotation(q) }
925    }
926
927    #[inline]
928    pub fn scaling(s: Vector3f) -> Matrix44f {
929        unsafe { ffi::whiteout_v_Matrix44f_scaling(s) }
930    }
931
932    #[inline]
933    pub fn compose(translation: Vector3f, rotation: Quaternion, scale: Vector3f) -> Matrix44f {
934        unsafe { ffi::whiteout_v_Matrix44f_compose(translation, rotation, scale) }
935    }
936
937    #[inline]
938    pub fn inverse(m: Matrix44f) -> Matrix44f {
939        unsafe { ffi::whiteout_v_Matrix44f_inverse(m) }
940    }
941
942    #[inline]
943    pub fn rotation_x(angle: f32) -> Matrix44f {
944        unsafe { ffi::whiteout_v_Matrix44f_rotation_x(angle) }
945    }
946
947    #[inline]
948    pub fn rotation_y(angle: f32) -> Matrix44f {
949        unsafe { ffi::whiteout_v_Matrix44f_rotation_y(angle) }
950    }
951
952    #[inline]
953    pub fn rotation_z(angle: f32) -> Matrix44f {
954        unsafe { ffi::whiteout_v_Matrix44f_rotation_z(angle) }
955    }
956
957    #[inline]
958    pub fn look_at_rh(eye: Vector3f, target: Vector3f, up: Vector3f) -> Matrix44f {
959        unsafe { ffi::whiteout_v_Matrix44f_look_at_rh(eye, target, up) }
960    }
961
962    #[inline]
963    pub fn look_at_lh(eye: Vector3f, target: Vector3f, up: Vector3f) -> Matrix44f {
964        unsafe { ffi::whiteout_v_Matrix44f_look_at_lh(eye, target, up) }
965    }
966
967    #[inline]
968    pub fn look_at_lh_sgcompat(eye: Vector3f, target: Vector3f, up: Vector3f) -> Matrix44f {
969        unsafe { ffi::whiteout_v_Matrix44f_look_at_lh_sgcompat(eye, target, up) }
970    }
971
972    #[inline]
973    pub fn perspective_fov_rh(fov_y: f32, aspect: f32, near_z: f32, far_z: f32) -> Matrix44f {
974        unsafe { ffi::whiteout_v_Matrix44f_perspective_fov_rh(fov_y, aspect, near_z, far_z) }
975    }
976
977    #[inline]
978    pub fn perspective_fov_lh(fov_y: f32, aspect: f32, near_z: f32, far_z: f32) -> Matrix44f {
979        unsafe { ffi::whiteout_v_Matrix44f_perspective_fov_lh(fov_y, aspect, near_z, far_z) }
980    }
981
982    #[inline]
983    pub fn perspective_diag_sgcompat(
984        fov_diagonal: f32,
985        aspect: f32,
986        near_z: f32,
987        far_z: f32,
988    ) -> Matrix44f {
989        unsafe {
990            ffi::whiteout_v_Matrix44f_perspective_diag_sgcompat(fov_diagonal, aspect, near_z, far_z)
991        }
992    }
993
994    #[inline]
995    pub fn orthographic_rh(width: f32, height: f32, near_z: f32, far_z: f32) -> Matrix44f {
996        unsafe { ffi::whiteout_v_Matrix44f_orthographic_rh(width, height, near_z, far_z) }
997    }
998
999    #[inline]
1000    pub fn transpose(self) -> Matrix44f {
1001        unsafe { ffi::whiteout_v_Matrix44f_transpose(self) }
1002    }
1003
1004    #[inline]
1005    pub fn extract_translation(self) -> Vector3f {
1006        unsafe { ffi::whiteout_v_Matrix44f_extract_translation(self) }
1007    }
1008
1009    #[inline]
1010    pub fn extract_rotation(self) -> Quaternion {
1011        unsafe { ffi::whiteout_v_Matrix44f_extract_rotation(self) }
1012    }
1013
1014    #[inline]
1015    pub fn extract_scale(self) -> Vector3f {
1016        unsafe { ffi::whiteout_v_Matrix44f_extract_scale(self) }
1017    }
1018}
1019
1020// ── Free functions ─────────────────────────────────────
1021
1022#[inline]
1023pub fn cross(a: Vector3f, b: Vector3f) -> Vector3f {
1024    unsafe { ffi::whiteout_v_cross(a, b) }
1025}
1026
1027#[inline]
1028pub fn transform_point(v: Vector3f, m: Matrix44f) -> Vector3f {
1029    unsafe { ffi::whiteout_v_transform_point(v, m) }
1030}
1031
1032#[inline]
1033pub fn transform_normal(v: Vector3f, m: Matrix44f) -> Vector3f {
1034    unsafe { ffi::whiteout_v_transform_normal(v, m) }
1035}
1036
1037/// Verify that the linked native library matches the layouts this crate
1038/// was generated against.
1039///
1040/// The `const` assertions above pin the Rust side at compile time; this
1041/// catches the other half — a `whiteout_native` built from different
1042/// headers or by a toolchain that lays the C++ types out differently.
1043pub fn check_abi() -> Result<(), crate::Error> {
1044    let l = unsafe { ffi::whiteout_v_layout() };
1045    if l.abi_version != ffi::ABI_VERSION {
1046        return Err(crate::Error::Layout {
1047            what: "abi_version",
1048            expected: ffi::ABI_VERSION as usize,
1049            actual: l.abi_version as usize,
1050        });
1051    }
1052    if l.vector2f as usize != core::mem::size_of::<Vector2f>() {
1053        return Err(crate::Error::Layout {
1054            what: "Vector2f",
1055            expected: core::mem::size_of::<Vector2f>(),
1056            actual: l.vector2f as usize,
1057        });
1058    }
1059    if l.vector3f as usize != core::mem::size_of::<Vector3f>() {
1060        return Err(crate::Error::Layout {
1061            what: "Vector3f",
1062            expected: core::mem::size_of::<Vector3f>(),
1063            actual: l.vector3f as usize,
1064        });
1065    }
1066    if l.vector4f as usize != core::mem::size_of::<Vector4f>() {
1067        return Err(crate::Error::Layout {
1068            what: "Vector4f",
1069            expected: core::mem::size_of::<Vector4f>(),
1070            actual: l.vector4f as usize,
1071        });
1072    }
1073    if l.quaternion as usize != core::mem::size_of::<Quaternion>() {
1074        return Err(crate::Error::Layout {
1075            what: "Quaternion",
1076            expected: core::mem::size_of::<Quaternion>(),
1077            actual: l.quaternion as usize,
1078        });
1079    }
1080    if l.matrix33f as usize != core::mem::size_of::<Matrix33f>() {
1081        return Err(crate::Error::Layout {
1082            what: "Matrix33f",
1083            expected: core::mem::size_of::<Matrix33f>(),
1084            actual: l.matrix33f as usize,
1085        });
1086    }
1087    if l.matrix44f as usize != core::mem::size_of::<Matrix44f>() {
1088        return Err(crate::Error::Layout {
1089            what: "Matrix44f",
1090            expected: core::mem::size_of::<Matrix44f>(),
1091            actual: l.matrix44f as usize,
1092        });
1093    }
1094    Ok(())
1095}
1096
1097/// Raw ABI surface.
1098///
1099/// Exposed so the parity tests can cross-check the natively-implemented
1100/// operations against the C++ ones. Not covered by semver — use the safe
1101/// API above.
1102#[doc(hidden)]
1103pub mod ffi {
1104    use super::*;
1105
1106    pub const ABI_VERSION: u32 = 2;
1107
1108    #[repr(C)]
1109    #[derive(Clone, Copy, Debug)]
1110    pub struct Layout {
1111        pub abi_version: u32,
1112        pub vector2f: u32,
1113        pub vector3f: u32,
1114        pub vector4f: u32,
1115        pub quaternion: u32,
1116        pub matrix33f: u32,
1117        pub matrix44f: u32,
1118    }
1119
1120    extern "C" {
1121        pub fn whiteout_v_layout() -> Layout;
1122        pub fn whiteout_v_Vector2f_dot(self_: Vector2f, other: Vector2f) -> f32;
1123        pub fn whiteout_v_Vector2f_length(self_: Vector2f) -> f32;
1124        pub fn whiteout_v_Vector2f_length_squared(self_: Vector2f) -> f32;
1125        pub fn whiteout_v_Vector2f_normalize(self_: *mut Vector2f);
1126        pub fn whiteout_v_Vector2f_normalized(self_: Vector2f) -> Vector2f;
1127        pub fn whiteout_v_Vector2f_add(self_: Vector2f, other: Vector2f) -> Vector2f;
1128        pub fn whiteout_v_Vector2f_sub(self_: Vector2f, other: Vector2f) -> Vector2f;
1129        pub fn whiteout_v_Vector2f_mul(self_: Vector2f, other: Vector2f) -> Vector2f;
1130        pub fn whiteout_v_Vector2f_div(self_: Vector2f, other: Vector2f) -> Vector2f;
1131        pub fn whiteout_v_Vector2f_mul_scalar(self_: Vector2f, scalar: f32) -> Vector2f;
1132        pub fn whiteout_v_Vector2f_div_scalar(self_: Vector2f, scalar: f32) -> Vector2f;
1133        pub fn whiteout_v_Vector2f_negate(self_: Vector2f) -> Vector2f;
1134        pub fn whiteout_v_Vector2f_lerp(start: Vector2f, end: Vector2f, t: f32) -> Vector2f;
1135        pub fn whiteout_v_Vector2f_tcb_in_tangent(
1136            prev: Vector2f,
1137            current: Vector2f,
1138            next: Vector2f,
1139            tension: f32,
1140            continuity: f32,
1141            bias: f32,
1142        ) -> Vector2f;
1143        pub fn whiteout_v_Vector2f_tcb_out_tangent(
1144            prev: Vector2f,
1145            current: Vector2f,
1146            next: Vector2f,
1147            tension: f32,
1148            continuity: f32,
1149            bias: f32,
1150        ) -> Vector2f;
1151        pub fn whiteout_v_Vector2f_bezier_lerp(
1152            start: Vector2f,
1153            outtan: Vector2f,
1154            intan: Vector2f,
1155            end: Vector2f,
1156            t: f32,
1157        ) -> Vector2f;
1158        pub fn whiteout_v_Vector2f_hermite_lerp(
1159            prev: Vector2f,
1160            start: Vector2f,
1161            end: Vector2f,
1162            next: Vector2f,
1163            t: f32,
1164        ) -> Vector2f;
1165        pub fn whiteout_v_Vector3f_dot(self_: Vector3f, other: Vector3f) -> f32;
1166        pub fn whiteout_v_Vector3f_length(self_: Vector3f) -> f32;
1167        pub fn whiteout_v_Vector3f_length_squared(self_: Vector3f) -> f32;
1168        pub fn whiteout_v_Vector3f_normalize(self_: *mut Vector3f);
1169        pub fn whiteout_v_Vector3f_normalized(self_: Vector3f) -> Vector3f;
1170        pub fn whiteout_v_Vector3f_add(self_: Vector3f, other: Vector3f) -> Vector3f;
1171        pub fn whiteout_v_Vector3f_sub(self_: Vector3f, other: Vector3f) -> Vector3f;
1172        pub fn whiteout_v_Vector3f_mul(self_: Vector3f, other: Vector3f) -> Vector3f;
1173        pub fn whiteout_v_Vector3f_div(self_: Vector3f, other: Vector3f) -> Vector3f;
1174        pub fn whiteout_v_Vector3f_mul_scalar(self_: Vector3f, scalar: f32) -> Vector3f;
1175        pub fn whiteout_v_Vector3f_div_scalar(self_: Vector3f, scalar: f32) -> Vector3f;
1176        pub fn whiteout_v_Vector3f_negate(self_: Vector3f) -> Vector3f;
1177        pub fn whiteout_v_Vector3f_lerp(start: Vector3f, end: Vector3f, t: f32) -> Vector3f;
1178        pub fn whiteout_v_Vector3f_tcb_in_tangent(
1179            prev: Vector3f,
1180            current: Vector3f,
1181            next: Vector3f,
1182            tension: f32,
1183            continuity: f32,
1184            bias: f32,
1185        ) -> Vector3f;
1186        pub fn whiteout_v_Vector3f_tcb_out_tangent(
1187            prev: Vector3f,
1188            current: Vector3f,
1189            next: Vector3f,
1190            tension: f32,
1191            continuity: f32,
1192            bias: f32,
1193        ) -> Vector3f;
1194        pub fn whiteout_v_Vector3f_bezier_lerp(
1195            start: Vector3f,
1196            outtan: Vector3f,
1197            intan: Vector3f,
1198            end: Vector3f,
1199            t: f32,
1200        ) -> Vector3f;
1201        pub fn whiteout_v_Vector3f_hermite_lerp(
1202            prev: Vector3f,
1203            start: Vector3f,
1204            end: Vector3f,
1205            next: Vector3f,
1206            t: f32,
1207        ) -> Vector3f;
1208        pub fn whiteout_v_Vector4f_dot(self_: Vector4f, other: Vector4f) -> f32;
1209        pub fn whiteout_v_Vector4f_length(self_: Vector4f) -> f32;
1210        pub fn whiteout_v_Vector4f_length_squared(self_: Vector4f) -> f32;
1211        pub fn whiteout_v_Vector4f_normalize(self_: *mut Vector4f);
1212        pub fn whiteout_v_Vector4f_normalized(self_: Vector4f) -> Vector4f;
1213        pub fn whiteout_v_Vector4f_add(self_: Vector4f, other: Vector4f) -> Vector4f;
1214        pub fn whiteout_v_Vector4f_sub(self_: Vector4f, other: Vector4f) -> Vector4f;
1215        pub fn whiteout_v_Vector4f_mul(self_: Vector4f, other: Vector4f) -> Vector4f;
1216        pub fn whiteout_v_Vector4f_div(self_: Vector4f, other: Vector4f) -> Vector4f;
1217        pub fn whiteout_v_Vector4f_mul_scalar(self_: Vector4f, scalar: f32) -> Vector4f;
1218        pub fn whiteout_v_Vector4f_div_scalar(self_: Vector4f, scalar: f32) -> Vector4f;
1219        pub fn whiteout_v_Vector4f_negate(self_: Vector4f) -> Vector4f;
1220        pub fn whiteout_v_Vector4f_lerp(start: Vector4f, end: Vector4f, t: f32) -> Vector4f;
1221        pub fn whiteout_v_Vector4f_tcb_in_tangent(
1222            prev: Vector4f,
1223            current: Vector4f,
1224            next: Vector4f,
1225            tension: f32,
1226            continuity: f32,
1227            bias: f32,
1228        ) -> Vector4f;
1229        pub fn whiteout_v_Vector4f_tcb_out_tangent(
1230            prev: Vector4f,
1231            current: Vector4f,
1232            next: Vector4f,
1233            tension: f32,
1234            continuity: f32,
1235            bias: f32,
1236        ) -> Vector4f;
1237        pub fn whiteout_v_Vector4f_bezier_lerp(
1238            start: Vector4f,
1239            outtan: Vector4f,
1240            intan: Vector4f,
1241            end: Vector4f,
1242            t: f32,
1243        ) -> Vector4f;
1244        pub fn whiteout_v_Vector4f_hermite_lerp(
1245            prev: Vector4f,
1246            start: Vector4f,
1247            end: Vector4f,
1248            next: Vector4f,
1249            t: f32,
1250        ) -> Vector4f;
1251        pub fn whiteout_v_Quaternion_dot(self_: Quaternion, other: Quaternion) -> f32;
1252        pub fn whiteout_v_Quaternion_length(self_: Quaternion) -> f32;
1253        pub fn whiteout_v_Quaternion_length_squared(self_: Quaternion) -> f32;
1254        pub fn whiteout_v_Quaternion_normalize(self_: *mut Quaternion);
1255        pub fn whiteout_v_Quaternion_normalized(self_: Quaternion) -> Quaternion;
1256        pub fn whiteout_v_Quaternion_add(self_: Quaternion, other: Quaternion) -> Quaternion;
1257        pub fn whiteout_v_Quaternion_sub(self_: Quaternion, other: Quaternion) -> Quaternion;
1258        pub fn whiteout_v_Quaternion_mul(self_: Quaternion, other: Quaternion) -> Quaternion;
1259        pub fn whiteout_v_Quaternion_mul_scalar(self_: Quaternion, scalar: f32) -> Quaternion;
1260        pub fn whiteout_v_Quaternion_div_scalar(self_: Quaternion, scalar: f32) -> Quaternion;
1261        pub fn whiteout_v_Quaternion_negate(self_: Quaternion) -> Quaternion;
1262        pub fn whiteout_v_Quaternion_conjugate(self_: Quaternion) -> Quaternion;
1263        pub fn whiteout_v_Quaternion_inverse(self_: Quaternion) -> Quaternion;
1264        pub fn whiteout_v_Quaternion_log(self_: Quaternion) -> Quaternion;
1265        pub fn whiteout_v_Quaternion_exp(self_: Quaternion) -> Quaternion;
1266        pub fn whiteout_v_Quaternion_to_euler_angles(self_: Quaternion) -> Vector3f;
1267        pub fn whiteout_v_Quaternion_rotate_vector(self_: Quaternion, v: Vector3f) -> Vector3f;
1268        pub fn whiteout_v_Quaternion_identity() -> Quaternion;
1269        pub fn whiteout_v_Quaternion_from_axis_angle(axis: Vector3f, angle_rad: f32) -> Quaternion;
1270        pub fn whiteout_v_Quaternion_from_euler_angles(euler_rad: Vector3f) -> Quaternion;
1271        pub fn whiteout_v_Quaternion_slerp(a: Quaternion, b: Quaternion, t: f32) -> Quaternion;
1272        pub fn whiteout_v_Quaternion_squad(
1273            start: Quaternion,
1274            outtan: Quaternion,
1275            inttan: Quaternion,
1276            end: Quaternion,
1277            t: f32,
1278        ) -> Quaternion;
1279        pub fn whiteout_v_Quaternion_ln_dif(a: Quaternion, b: Quaternion) -> Quaternion;
1280        pub fn whiteout_v_Matrix33f_add(self_: Matrix33f, other: Matrix33f) -> Matrix33f;
1281        pub fn whiteout_v_Matrix33f_sub(self_: Matrix33f, other: Matrix33f) -> Matrix33f;
1282        pub fn whiteout_v_Matrix33f_mul(self_: Matrix33f, other: Matrix33f) -> Matrix33f;
1283        pub fn whiteout_v_Matrix33f_mul_scalar(self_: Matrix33f, scalar: f32) -> Matrix33f;
1284        pub fn whiteout_v_Matrix33f_identity() -> Matrix33f;
1285        pub fn whiteout_v_Matrix33f_zero() -> Matrix33f;
1286        pub fn whiteout_v_Matrix44f_add(self_: Matrix44f, other: Matrix44f) -> Matrix44f;
1287        pub fn whiteout_v_Matrix44f_sub(self_: Matrix44f, other: Matrix44f) -> Matrix44f;
1288        pub fn whiteout_v_Matrix44f_mul(self_: Matrix44f, other: Matrix44f) -> Matrix44f;
1289        pub fn whiteout_v_Matrix44f_mul_scalar(self_: Matrix44f, scalar: f32) -> Matrix44f;
1290        pub fn whiteout_v_Matrix44f_identity() -> Matrix44f;
1291        pub fn whiteout_v_Matrix44f_zero() -> Matrix44f;
1292        pub fn whiteout_v_Matrix44f_translation(t: Vector3f) -> Matrix44f;
1293        pub fn whiteout_v_Matrix44f_rotation(q: Quaternion) -> Matrix44f;
1294        pub fn whiteout_v_Matrix44f_scaling(s: Vector3f) -> Matrix44f;
1295        pub fn whiteout_v_Matrix44f_compose(
1296            translation: Vector3f,
1297            rotation: Quaternion,
1298            scale: Vector3f,
1299        ) -> Matrix44f;
1300        pub fn whiteout_v_Matrix44f_inverse(m: Matrix44f) -> Matrix44f;
1301        pub fn whiteout_v_Matrix44f_rotation_x(angle: f32) -> Matrix44f;
1302        pub fn whiteout_v_Matrix44f_rotation_y(angle: f32) -> Matrix44f;
1303        pub fn whiteout_v_Matrix44f_rotation_z(angle: f32) -> Matrix44f;
1304        pub fn whiteout_v_Matrix44f_look_at_rh(
1305            eye: Vector3f,
1306            target: Vector3f,
1307            up: Vector3f,
1308        ) -> Matrix44f;
1309        pub fn whiteout_v_Matrix44f_look_at_lh(
1310            eye: Vector3f,
1311            target: Vector3f,
1312            up: Vector3f,
1313        ) -> Matrix44f;
1314        pub fn whiteout_v_Matrix44f_look_at_lh_sgcompat(
1315            eye: Vector3f,
1316            target: Vector3f,
1317            up: Vector3f,
1318        ) -> Matrix44f;
1319        pub fn whiteout_v_Matrix44f_perspective_fov_rh(
1320            fov_y: f32,
1321            aspect: f32,
1322            near_z: f32,
1323            far_z: f32,
1324        ) -> Matrix44f;
1325        pub fn whiteout_v_Matrix44f_perspective_fov_lh(
1326            fov_y: f32,
1327            aspect: f32,
1328            near_z: f32,
1329            far_z: f32,
1330        ) -> Matrix44f;
1331        pub fn whiteout_v_Matrix44f_perspective_diag_sgcompat(
1332            fov_diagonal: f32,
1333            aspect: f32,
1334            near_z: f32,
1335            far_z: f32,
1336        ) -> Matrix44f;
1337        pub fn whiteout_v_Matrix44f_orthographic_rh(
1338            width: f32,
1339            height: f32,
1340            near_z: f32,
1341            far_z: f32,
1342        ) -> Matrix44f;
1343        pub fn whiteout_v_Matrix44f_transpose(self_: Matrix44f) -> Matrix44f;
1344        pub fn whiteout_v_Matrix44f_extract_translation(self_: Matrix44f) -> Vector3f;
1345        pub fn whiteout_v_Matrix44f_extract_rotation(self_: Matrix44f) -> Quaternion;
1346        pub fn whiteout_v_Matrix44f_extract_scale(self_: Matrix44f) -> Vector3f;
1347        pub fn whiteout_v_cross(a: Vector3f, b: Vector3f) -> Vector3f;
1348        pub fn whiteout_v_transform_point(v: Vector3f, m: Matrix44f) -> Vector3f;
1349        pub fn whiteout_v_transform_normal(v: Vector3f, m: Matrix44f) -> Vector3f;
1350    }
1351}