Skip to main content

goud_engine/core/
math.rs

1//! FFI-safe mathematical types for the engine.
2//!
3//! This module provides `#[repr(C)]` mathematical types that are safe to pass across
4//! FFI boundaries. These types wrap functionality from cgmath but guarantee a stable,
5//! predictable memory layout for use with C#, Python, and other language bindings.
6//!
7//! # Design Decision
8//!
9//! We wrap cgmath rather than replacing it because:
10//! 1. **Internal Operations**: cgmath provides battle-tested matrix/vector operations
11//!    (look_at, quaternion math, etc.) that would be error-prone to reimplement
12//! 2. **FFI Safety**: cgmath types like `Vector3<f32>` are newtypes over arrays and
13//!    don't guarantee a specific memory layout suitable for FFI
14//! 3. **Type Safety**: Our wrappers ensure compile-time FFI compatibility while
15//!    maintaining ergonomic conversions for internal use
16//!
17//! # Usage
18//!
19//! ```rust
20//! use goud_engine::core::math::{Vec3, Color};
21//!
22//! // Create FFI-safe types
23//! let position = Vec3::new(1.0, 2.0, 3.0);
24//! let color = Color::RED;
25//!
26//! // Convert to cgmath for internal math operations
27//! let cgmath_vec: cgmath::Vector3<f32> = position.into();
28//!
29//! // Convert back from cgmath results
30//! let result = Vec3::from(cgmath_vec);
31//! ```
32
33use std::ops::{Add, Div, Mul, Neg, Sub};
34
35// Re-export cgmath types for internal use where FFI is not needed
36pub use cgmath::{Matrix3, Matrix4, Point3, Quaternion};
37
38// =============================================================================
39// Vec2 - 2D Vector (FFI-Safe)
40// =============================================================================
41
42/// A 2D vector with FFI-safe memory layout.
43///
44/// This type is guaranteed to have the same memory layout as a C struct
45/// with two consecutive f32 fields. Use this type for any 2D positions,
46/// velocities, or texture coordinates that cross FFI boundaries.
47#[repr(C)]
48#[derive(Clone, Copy, Debug, PartialEq, Default)]
49pub struct Vec2 {
50    /// The x-component of the vector.
51    pub x: f32,
52    /// The y-component of the vector.
53    pub y: f32,
54}
55
56impl Vec2 {
57    /// Creates a new Vec2 from x and y components.
58    #[inline]
59    pub const fn new(x: f32, y: f32) -> Self {
60        Self { x, y }
61    }
62
63    /// Returns the zero vector (0, 0).
64    #[inline]
65    pub const fn zero() -> Self {
66        Self { x: 0.0, y: 0.0 }
67    }
68
69    /// Returns the one vector (1, 1).
70    #[inline]
71    pub const fn one() -> Self {
72        Self { x: 1.0, y: 1.0 }
73    }
74
75    /// Returns the unit X vector (1, 0).
76    #[inline]
77    pub const fn unit_x() -> Self {
78        Self { x: 1.0, y: 0.0 }
79    }
80
81    /// Returns the unit Y vector (0, 1).
82    #[inline]
83    pub const fn unit_y() -> Self {
84        Self { x: 0.0, y: 1.0 }
85    }
86
87    /// Computes the dot product of two vectors.
88    #[inline]
89    pub fn dot(self, other: Self) -> f32 {
90        self.x * other.x + self.y * other.y
91    }
92
93    /// Returns the squared length of the vector.
94    ///
95    /// This is more efficient than `length()` when you only need to compare lengths.
96    #[inline]
97    pub fn length_squared(self) -> f32 {
98        self.dot(self)
99    }
100
101    /// Returns the length (magnitude) of the vector.
102    #[inline]
103    pub fn length(self) -> f32 {
104        self.length_squared().sqrt()
105    }
106
107    /// Returns a normalized (unit length) version of this vector.
108    ///
109    /// If the vector has zero length, returns the zero vector.
110    #[inline]
111    pub fn normalize(self) -> Self {
112        let len = self.length();
113        if len == 0.0 {
114            Self::zero()
115        } else {
116            self / len
117        }
118    }
119
120    /// Linearly interpolates between two vectors.
121    ///
122    /// When `t = 0.0`, returns `self`. When `t = 1.0`, returns `other`.
123    #[inline]
124    pub fn lerp(self, other: Self, t: f32) -> Self {
125        Self {
126            x: self.x + (other.x - self.x) * t,
127            y: self.y + (other.y - self.y) * t,
128        }
129    }
130
131    /// Returns the perpendicular vector (rotated 90 degrees counter-clockwise).
132    #[inline]
133    pub fn perpendicular(self) -> Self {
134        Self {
135            x: -self.y,
136            y: self.x,
137        }
138    }
139}
140
141// Operator implementations for Vec2
142impl Add for Vec2 {
143    type Output = Self;
144    #[inline]
145    fn add(self, other: Self) -> Self {
146        Self {
147            x: self.x + other.x,
148            y: self.y + other.y,
149        }
150    }
151}
152
153impl Sub for Vec2 {
154    type Output = Self;
155    #[inline]
156    fn sub(self, other: Self) -> Self {
157        Self {
158            x: self.x - other.x,
159            y: self.y - other.y,
160        }
161    }
162}
163
164impl Mul<f32> for Vec2 {
165    type Output = Self;
166    #[inline]
167    fn mul(self, scalar: f32) -> Self {
168        Self {
169            x: self.x * scalar,
170            y: self.y * scalar,
171        }
172    }
173}
174
175impl Mul<Vec2> for f32 {
176    type Output = Vec2;
177    #[inline]
178    fn mul(self, vec: Vec2) -> Vec2 {
179        vec * self
180    }
181}
182
183impl Div<f32> for Vec2 {
184    type Output = Self;
185    #[inline]
186    fn div(self, scalar: f32) -> Self {
187        Self {
188            x: self.x / scalar,
189            y: self.y / scalar,
190        }
191    }
192}
193
194impl Neg for Vec2 {
195    type Output = Self;
196    #[inline]
197    fn neg(self) -> Self {
198        Self {
199            x: -self.x,
200            y: -self.y,
201        }
202    }
203}
204
205// cgmath conversions for Vec2
206impl From<cgmath::Vector2<f32>> for Vec2 {
207    #[inline]
208    fn from(v: cgmath::Vector2<f32>) -> Self {
209        Self { x: v.x, y: v.y }
210    }
211}
212
213impl From<Vec2> for cgmath::Vector2<f32> {
214    #[inline]
215    fn from(v: Vec2) -> Self {
216        cgmath::Vector2::new(v.x, v.y)
217    }
218}
219
220// =============================================================================
221// Vec3 - 3D Vector (FFI-Safe)
222// =============================================================================
223
224/// A 3D vector with FFI-safe memory layout.
225///
226/// This type is guaranteed to have the same memory layout as a C struct
227/// with three consecutive f32 fields. Use this type for any 3D positions,
228/// directions, or colors that cross FFI boundaries.
229#[repr(C)]
230#[derive(Clone, Copy, Debug, PartialEq, Default)]
231pub struct Vec3 {
232    /// The x-component of the vector.
233    pub x: f32,
234    /// The y-component of the vector.
235    pub y: f32,
236    /// The z-component of the vector.
237    pub z: f32,
238}
239
240impl Vec3 {
241    /// Creates a new Vec3 from x, y, z components.
242    #[inline]
243    pub const fn new(x: f32, y: f32, z: f32) -> Self {
244        Self { x, y, z }
245    }
246
247    /// Returns the zero vector (0, 0, 0).
248    #[inline]
249    pub const fn zero() -> Self {
250        Self {
251            x: 0.0,
252            y: 0.0,
253            z: 0.0,
254        }
255    }
256
257    /// Returns the one vector (1, 1, 1).
258    #[inline]
259    pub const fn one() -> Self {
260        Self {
261            x: 1.0,
262            y: 1.0,
263            z: 1.0,
264        }
265    }
266
267    /// Returns the unit X vector (1, 0, 0).
268    #[inline]
269    pub const fn unit_x() -> Self {
270        Self {
271            x: 1.0,
272            y: 0.0,
273            z: 0.0,
274        }
275    }
276
277    /// Returns the unit Y vector (0, 1, 0).
278    #[inline]
279    pub const fn unit_y() -> Self {
280        Self {
281            x: 0.0,
282            y: 1.0,
283            z: 0.0,
284        }
285    }
286
287    /// Returns the unit Z vector (0, 0, 1).
288    #[inline]
289    pub const fn unit_z() -> Self {
290        Self {
291            x: 0.0,
292            y: 0.0,
293            z: 1.0,
294        }
295    }
296
297    /// Computes the dot product of two vectors.
298    #[inline]
299    pub fn dot(self, other: Self) -> f32 {
300        self.x * other.x + self.y * other.y + self.z * other.z
301    }
302
303    /// Computes the cross product of two vectors.
304    ///
305    /// The result is perpendicular to both input vectors, following the right-hand rule.
306    #[inline]
307    pub fn cross(self, other: Self) -> Self {
308        Self {
309            x: self.y * other.z - self.z * other.y,
310            y: self.z * other.x - self.x * other.z,
311            z: self.x * other.y - self.y * other.x,
312        }
313    }
314
315    /// Returns the squared length of the vector.
316    #[inline]
317    pub fn length_squared(self) -> f32 {
318        self.dot(self)
319    }
320
321    /// Returns the length (magnitude) of the vector.
322    #[inline]
323    pub fn length(self) -> f32 {
324        self.length_squared().sqrt()
325    }
326
327    /// Returns a normalized (unit length) version of this vector.
328    ///
329    /// If the vector has zero length, returns the zero vector.
330    #[inline]
331    pub fn normalize(self) -> Self {
332        let len = self.length();
333        if len == 0.0 {
334            Self::zero()
335        } else {
336            self / len
337        }
338    }
339
340    /// Linearly interpolates between two vectors.
341    #[inline]
342    pub fn lerp(self, other: Self, t: f32) -> Self {
343        Self {
344            x: self.x + (other.x - self.x) * t,
345            y: self.y + (other.y - self.y) * t,
346            z: self.z + (other.z - self.z) * t,
347        }
348    }
349}
350
351// Operator implementations for Vec3
352impl Add for Vec3 {
353    type Output = Self;
354    #[inline]
355    fn add(self, other: Self) -> Self {
356        Self {
357            x: self.x + other.x,
358            y: self.y + other.y,
359            z: self.z + other.z,
360        }
361    }
362}
363
364impl Sub for Vec3 {
365    type Output = Self;
366    #[inline]
367    fn sub(self, other: Self) -> Self {
368        Self {
369            x: self.x - other.x,
370            y: self.y - other.y,
371            z: self.z - other.z,
372        }
373    }
374}
375
376impl Mul<f32> for Vec3 {
377    type Output = Self;
378    #[inline]
379    fn mul(self, scalar: f32) -> Self {
380        Self {
381            x: self.x * scalar,
382            y: self.y * scalar,
383            z: self.z * scalar,
384        }
385    }
386}
387
388impl Mul<Vec3> for f32 {
389    type Output = Vec3;
390    #[inline]
391    fn mul(self, vec: Vec3) -> Vec3 {
392        vec * self
393    }
394}
395
396impl Div<f32> for Vec3 {
397    type Output = Self;
398    #[inline]
399    fn div(self, scalar: f32) -> Self {
400        Self {
401            x: self.x / scalar,
402            y: self.y / scalar,
403            z: self.z / scalar,
404        }
405    }
406}
407
408impl Neg for Vec3 {
409    type Output = Self;
410    #[inline]
411    fn neg(self) -> Self {
412        Self {
413            x: -self.x,
414            y: -self.y,
415            z: -self.z,
416        }
417    }
418}
419
420// cgmath conversions for Vec3
421impl From<cgmath::Vector3<f32>> for Vec3 {
422    #[inline]
423    fn from(v: cgmath::Vector3<f32>) -> Self {
424        Self {
425            x: v.x,
426            y: v.y,
427            z: v.z,
428        }
429    }
430}
431
432impl From<Vec3> for cgmath::Vector3<f32> {
433    #[inline]
434    fn from(v: Vec3) -> Self {
435        cgmath::Vector3::new(v.x, v.y, v.z)
436    }
437}
438
439// =============================================================================
440// Vec4 - 4D Vector (FFI-Safe)
441// =============================================================================
442
443/// A 4D vector with FFI-safe memory layout.
444///
445/// This type is commonly used for homogeneous coordinates in graphics
446/// or RGBA colors. It is guaranteed to have the same memory layout as
447/// a C struct with four consecutive f32 fields.
448#[repr(C)]
449#[derive(Clone, Copy, Debug, PartialEq, Default)]
450pub struct Vec4 {
451    /// The x-component of the vector.
452    pub x: f32,
453    /// The y-component of the vector.
454    pub y: f32,
455    /// The z-component of the vector.
456    pub z: f32,
457    /// The w-component of the vector.
458    pub w: f32,
459}
460
461impl Vec4 {
462    /// Creates a new Vec4 from x, y, z, w components.
463    #[inline]
464    pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
465        Self { x, y, z, w }
466    }
467
468    /// Returns the zero vector (0, 0, 0, 0).
469    #[inline]
470    pub const fn zero() -> Self {
471        Self {
472            x: 0.0,
473            y: 0.0,
474            z: 0.0,
475            w: 0.0,
476        }
477    }
478
479    /// Returns the one vector (1, 1, 1, 1).
480    #[inline]
481    pub const fn one() -> Self {
482        Self {
483            x: 1.0,
484            y: 1.0,
485            z: 1.0,
486            w: 1.0,
487        }
488    }
489
490    /// Creates a Vec4 from a Vec3 and w component.
491    #[inline]
492    pub const fn from_vec3(v: Vec3, w: f32) -> Self {
493        Self {
494            x: v.x,
495            y: v.y,
496            z: v.z,
497            w,
498        }
499    }
500
501    /// Returns the xyz components as a Vec3.
502    #[inline]
503    pub const fn xyz(self) -> Vec3 {
504        Vec3 {
505            x: self.x,
506            y: self.y,
507            z: self.z,
508        }
509    }
510
511    /// Computes the dot product of two vectors.
512    #[inline]
513    pub fn dot(self, other: Self) -> f32 {
514        self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w
515    }
516
517    /// Returns the squared length of the vector.
518    #[inline]
519    pub fn length_squared(self) -> f32 {
520        self.dot(self)
521    }
522
523    /// Returns the length (magnitude) of the vector.
524    #[inline]
525    pub fn length(self) -> f32 {
526        self.length_squared().sqrt()
527    }
528
529    /// Returns a normalized (unit length) version of this vector.
530    #[inline]
531    pub fn normalize(self) -> Self {
532        let len = self.length();
533        if len == 0.0 {
534            Self::zero()
535        } else {
536            self / len
537        }
538    }
539
540    /// Linearly interpolates between two vectors.
541    #[inline]
542    pub fn lerp(self, other: Self, t: f32) -> Self {
543        Self {
544            x: self.x + (other.x - self.x) * t,
545            y: self.y + (other.y - self.y) * t,
546            z: self.z + (other.z - self.z) * t,
547            w: self.w + (other.w - self.w) * t,
548        }
549    }
550}
551
552// Operator implementations for Vec4
553impl Add for Vec4 {
554    type Output = Self;
555    #[inline]
556    fn add(self, other: Self) -> Self {
557        Self {
558            x: self.x + other.x,
559            y: self.y + other.y,
560            z: self.z + other.z,
561            w: self.w + other.w,
562        }
563    }
564}
565
566impl Sub for Vec4 {
567    type Output = Self;
568    #[inline]
569    fn sub(self, other: Self) -> Self {
570        Self {
571            x: self.x - other.x,
572            y: self.y - other.y,
573            z: self.z - other.z,
574            w: self.w - other.w,
575        }
576    }
577}
578
579impl Mul<f32> for Vec4 {
580    type Output = Self;
581    #[inline]
582    fn mul(self, scalar: f32) -> Self {
583        Self {
584            x: self.x * scalar,
585            y: self.y * scalar,
586            z: self.z * scalar,
587            w: self.w * scalar,
588        }
589    }
590}
591
592impl Mul<Vec4> for f32 {
593    type Output = Vec4;
594    #[inline]
595    fn mul(self, vec: Vec4) -> Vec4 {
596        vec * self
597    }
598}
599
600impl Div<f32> for Vec4 {
601    type Output = Self;
602    #[inline]
603    fn div(self, scalar: f32) -> Self {
604        Self {
605            x: self.x / scalar,
606            y: self.y / scalar,
607            z: self.z / scalar,
608            w: self.w / scalar,
609        }
610    }
611}
612
613impl Neg for Vec4 {
614    type Output = Self;
615    #[inline]
616    fn neg(self) -> Self {
617        Self {
618            x: -self.x,
619            y: -self.y,
620            z: -self.z,
621            w: -self.w,
622        }
623    }
624}
625
626// cgmath conversions for Vec4
627impl From<cgmath::Vector4<f32>> for Vec4 {
628    #[inline]
629    fn from(v: cgmath::Vector4<f32>) -> Self {
630        Self {
631            x: v.x,
632            y: v.y,
633            z: v.z,
634            w: v.w,
635        }
636    }
637}
638
639impl From<Vec4> for cgmath::Vector4<f32> {
640    #[inline]
641    fn from(v: Vec4) -> Self {
642        cgmath::Vector4::new(v.x, v.y, v.z, v.w)
643    }
644}
645
646// =============================================================================
647// Rect - 2D Rectangle (FFI-Safe)
648// =============================================================================
649
650/// A 2D rectangle with FFI-safe memory layout.
651///
652/// Defined by position (x, y) and size (width, height).
653/// The position represents the top-left corner in screen space.
654#[repr(C)]
655#[derive(Clone, Copy, Debug, PartialEq, Default)]
656pub struct Rect {
657    /// The x-coordinate of the top-left corner of the rectangle.
658    pub x: f32,
659    /// The y-coordinate of the top-left corner of the rectangle.
660    pub y: f32,
661    /// The width of the rectangle.
662    pub width: f32,
663    /// The height of the rectangle.
664    pub height: f32,
665}
666
667impl Rect {
668    /// Creates a new rectangle from position and size.
669    #[inline]
670    pub const fn new(x: f32, y: f32, width: f32, height: f32) -> Self {
671        Self {
672            x,
673            y,
674            width,
675            height,
676        }
677    }
678
679    /// Creates a rectangle from min and max points.
680    #[inline]
681    pub fn from_min_max(min: Vec2, max: Vec2) -> Self {
682        Self {
683            x: min.x,
684            y: min.y,
685            width: max.x - min.x,
686            height: max.y - min.y,
687        }
688    }
689
690    /// Creates a unit rectangle (0, 0, 1, 1).
691    #[inline]
692    pub const fn unit() -> Self {
693        Self {
694            x: 0.0,
695            y: 0.0,
696            width: 1.0,
697            height: 1.0,
698        }
699    }
700
701    /// Returns the minimum point (top-left corner).
702    #[inline]
703    pub const fn min(&self) -> Vec2 {
704        Vec2 {
705            x: self.x,
706            y: self.y,
707        }
708    }
709
710    /// Returns the maximum point (bottom-right corner).
711    #[inline]
712    pub fn max(&self) -> Vec2 {
713        Vec2 {
714            x: self.x + self.width,
715            y: self.y + self.height,
716        }
717    }
718
719    /// Returns the center point of the rectangle.
720    #[inline]
721    pub fn center(&self) -> Vec2 {
722        Vec2 {
723            x: self.x + self.width * 0.5,
724            y: self.y + self.height * 0.5,
725        }
726    }
727
728    /// Returns the size as a Vec2.
729    #[inline]
730    pub const fn size(&self) -> Vec2 {
731        Vec2 {
732            x: self.width,
733            y: self.height,
734        }
735    }
736
737    /// Returns the area of the rectangle.
738    #[inline]
739    pub fn area(&self) -> f32 {
740        self.width * self.height
741    }
742
743    /// Checks if the rectangle contains a point.
744    #[inline]
745    pub fn contains(&self, point: Vec2) -> bool {
746        point.x >= self.x
747            && point.x < self.x + self.width
748            && point.y >= self.y
749            && point.y < self.y + self.height
750    }
751
752    /// Checks if this rectangle intersects with another.
753    #[inline]
754    pub fn intersects(&self, other: &Rect) -> bool {
755        self.x < other.x + other.width
756            && self.x + self.width > other.x
757            && self.y < other.y + other.height
758            && self.y + self.height > other.y
759    }
760
761    /// Returns the intersection of two rectangles, or None if they don't intersect.
762    pub fn intersection(&self, other: &Rect) -> Option<Rect> {
763        let x = self.x.max(other.x);
764        let y = self.y.max(other.y);
765        let max_x = (self.x + self.width).min(other.x + other.width);
766        let max_y = (self.y + self.height).min(other.y + other.height);
767
768        if x < max_x && y < max_y {
769            Some(Rect {
770                x,
771                y,
772                width: max_x - x,
773                height: max_y - y,
774            })
775        } else {
776            None
777        }
778    }
779}
780
781// =============================================================================
782// Color - RGBA Color (FFI-Safe)
783// =============================================================================
784
785/// An RGBA color with FFI-safe memory layout.
786///
787/// Components are stored as f32 values, typically in the range [0.0, 1.0].
788/// Values outside this range are allowed for HDR rendering.
789#[repr(C)]
790#[derive(Clone, Copy, Debug, PartialEq, Default)]
791pub struct Color {
792    /// The red component of the color.
793    pub r: f32,
794    /// The green component of the color.
795    pub g: f32,
796    /// The blue component of the color.
797    pub b: f32,
798    /// The alpha (transparency) component of the color.
799    pub a: f32,
800}
801
802impl Color {
803    // Common color constants
804    /// The color white (1.0, 1.0, 1.0, 1.0).
805    pub const WHITE: Color = Color::rgb(1.0, 1.0, 1.0);
806    /// The color black (0.0, 0.0, 0.0, 1.0).
807    pub const BLACK: Color = Color::rgb(0.0, 0.0, 0.0);
808    /// The color red (1.0, 0.0, 0.0, 1.0).
809    pub const RED: Color = Color::rgb(1.0, 0.0, 0.0);
810    /// The color green (0.0, 1.0, 0.0, 1.0).
811    pub const GREEN: Color = Color::rgb(0.0, 1.0, 0.0);
812    /// The color blue (0.0, 0.0, 1.0, 1.0).
813    pub const BLUE: Color = Color::rgb(0.0, 0.0, 1.0);
814    /// The color yellow (1.0, 1.0, 0.0, 1.0).
815    pub const YELLOW: Color = Color::rgb(1.0, 1.0, 0.0);
816    /// The color cyan (0.0, 1.0, 1.0, 1.0).
817    pub const CYAN: Color = Color::rgb(0.0, 1.0, 1.0);
818    /// The color magenta (1.0, 0.0, 1.0, 1.0).
819    pub const MAGENTA: Color = Color::rgb(1.0, 0.0, 1.0);
820    /// Transparent black (0.0, 0.0, 0.0, 0.0).
821    pub const TRANSPARENT: Color = Color::rgba(0.0, 0.0, 0.0, 0.0);
822    /// The color gray (0.5, 0.5, 0.5, 1.0).
823    pub const GRAY: Color = Color::rgb(0.5, 0.5, 0.5);
824
825    /// Creates a new RGBA color.
826    #[inline]
827    pub const fn new(r: f32, g: f32, b: f32, a: f32) -> Self {
828        Self { r, g, b, a }
829    }
830
831    /// Creates an RGB color with alpha = 1.0.
832    #[inline]
833    pub const fn rgb(r: f32, g: f32, b: f32) -> Self {
834        Self { r, g, b, a: 1.0 }
835    }
836
837    /// Creates an RGBA color.
838    #[inline]
839    pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self {
840        Self { r, g, b, a }
841    }
842
843    /// Creates a color from 8-bit RGBA values (0-255).
844    #[inline]
845    pub fn from_u8(r: u8, g: u8, b: u8, a: u8) -> Self {
846        Self {
847            r: r as f32 / 255.0,
848            g: g as f32 / 255.0,
849            b: b as f32 / 255.0,
850            a: a as f32 / 255.0,
851        }
852    }
853
854    /// Creates a color from a hex value (0xRRGGBB or 0xRRGGBBAA).
855    #[inline]
856    pub fn from_hex(hex: u32) -> Self {
857        if hex > 0xFFFFFF {
858            // Has alpha (0xRRGGBBAA)
859            Self::from_u8(
860                ((hex >> 24) & 0xFF) as u8,
861                ((hex >> 16) & 0xFF) as u8,
862                ((hex >> 8) & 0xFF) as u8,
863                (hex & 0xFF) as u8,
864            )
865        } else {
866            // No alpha (0xRRGGBB)
867            Self::from_u8(
868                ((hex >> 16) & 0xFF) as u8,
869                ((hex >> 8) & 0xFF) as u8,
870                (hex & 0xFF) as u8,
871                255,
872            )
873        }
874    }
875
876    /// Returns the RGB components as a Vec3.
877    #[inline]
878    pub const fn to_vec3(&self) -> Vec3 {
879        Vec3 {
880            x: self.r,
881            y: self.g,
882            z: self.b,
883        }
884    }
885
886    /// Returns all RGBA components as a Vec4.
887    #[inline]
888    pub const fn to_vec4(&self) -> Vec4 {
889        Vec4 {
890            x: self.r,
891            y: self.g,
892            z: self.b,
893            w: self.a,
894        }
895    }
896
897    /// Creates a color from a Vec3 (RGB) with alpha = 1.0.
898    #[inline]
899    pub const fn from_vec3(v: Vec3) -> Self {
900        Self {
901            r: v.x,
902            g: v.y,
903            b: v.z,
904            a: 1.0,
905        }
906    }
907
908    /// Creates a color from a Vec4 (RGBA).
909    #[inline]
910    pub const fn from_vec4(v: Vec4) -> Self {
911        Self {
912            r: v.x,
913            g: v.y,
914            b: v.z,
915            a: v.w,
916        }
917    }
918
919    /// Linearly interpolates between two colors.
920    #[inline]
921    pub fn lerp(self, other: Self, t: f32) -> Self {
922        Self {
923            r: self.r + (other.r - self.r) * t,
924            g: self.g + (other.g - self.g) * t,
925            b: self.b + (other.b - self.b) * t,
926            a: self.a + (other.a - self.a) * t,
927        }
928    }
929
930    /// Returns a new color with the specified alpha.
931    #[inline]
932    pub const fn with_alpha(self, a: f32) -> Self {
933        Self {
934            r: self.r,
935            g: self.g,
936            b: self.b,
937            a,
938        }
939    }
940
941    /// Clamps all components to the [0.0, 1.0] range.
942    #[inline]
943    pub fn clamp(self) -> Self {
944        Self {
945            r: self.r.clamp(0.0, 1.0),
946            g: self.g.clamp(0.0, 1.0),
947            b: self.b.clamp(0.0, 1.0),
948            a: self.a.clamp(0.0, 1.0),
949        }
950    }
951}
952
953// =============================================================================
954// Tests
955// =============================================================================
956
957#[cfg(test)]
958mod tests {
959    use super::*;
960
961    // =========================================================================
962    // Vec2 Tests
963    // =========================================================================
964
965    #[test]
966    fn test_vec2_constructors() {
967        assert_eq!(Vec2::new(1.0, 2.0), Vec2 { x: 1.0, y: 2.0 });
968        assert_eq!(Vec2::zero(), Vec2 { x: 0.0, y: 0.0 });
969        assert_eq!(Vec2::one(), Vec2 { x: 1.0, y: 1.0 });
970        assert_eq!(Vec2::unit_x(), Vec2 { x: 1.0, y: 0.0 });
971        assert_eq!(Vec2::unit_y(), Vec2 { x: 0.0, y: 1.0 });
972    }
973
974    #[test]
975    fn test_vec2_dot() {
976        let a = Vec2::new(2.0, 3.0);
977        let b = Vec2::new(4.0, 5.0);
978        assert_eq!(a.dot(b), 23.0); // 2*4 + 3*5 = 8 + 15 = 23
979    }
980
981    #[test]
982    fn test_vec2_length() {
983        let v = Vec2::new(3.0, 4.0);
984        assert_eq!(v.length_squared(), 25.0);
985        assert_eq!(v.length(), 5.0);
986    }
987
988    #[test]
989    fn test_vec2_normalize() {
990        let v = Vec2::new(3.0, 4.0);
991        let n = v.normalize();
992        assert!((n.length() - 1.0).abs() < 0.0001);
993        assert_eq!(Vec2::zero().normalize(), Vec2::zero());
994    }
995
996    #[test]
997    fn test_vec2_operators() {
998        let a = Vec2::new(1.0, 2.0);
999        let b = Vec2::new(3.0, 4.0);
1000
1001        assert_eq!(a + b, Vec2::new(4.0, 6.0));
1002        assert_eq!(a - b, Vec2::new(-2.0, -2.0));
1003        assert_eq!(a * 2.0, Vec2::new(2.0, 4.0));
1004        assert_eq!(2.0 * a, Vec2::new(2.0, 4.0));
1005        assert_eq!(a / 2.0, Vec2::new(0.5, 1.0));
1006        assert_eq!(-a, Vec2::new(-1.0, -2.0));
1007    }
1008
1009    #[test]
1010    fn test_vec2_lerp() {
1011        let a = Vec2::new(0.0, 0.0);
1012        let b = Vec2::new(10.0, 20.0);
1013        assert_eq!(a.lerp(b, 0.0), a);
1014        assert_eq!(a.lerp(b, 1.0), b);
1015        assert_eq!(a.lerp(b, 0.5), Vec2::new(5.0, 10.0));
1016    }
1017
1018    #[test]
1019    fn test_vec2_cgmath_conversion() {
1020        let goud = Vec2::new(1.0, 2.0);
1021        let cg: cgmath::Vector2<f32> = goud.into();
1022        assert_eq!(cg.x, 1.0);
1023        assert_eq!(cg.y, 2.0);
1024
1025        let back: Vec2 = cg.into();
1026        assert_eq!(back, goud);
1027    }
1028
1029    // =========================================================================
1030    // Vec3 Tests
1031    // =========================================================================
1032
1033    #[test]
1034    fn test_vec3_constructors() {
1035        assert_eq!(
1036            Vec3::new(1.0, 2.0, 3.0),
1037            Vec3 {
1038                x: 1.0,
1039                y: 2.0,
1040                z: 3.0
1041            }
1042        );
1043        assert_eq!(
1044            Vec3::zero(),
1045            Vec3 {
1046                x: 0.0,
1047                y: 0.0,
1048                z: 0.0
1049            }
1050        );
1051        assert_eq!(
1052            Vec3::one(),
1053            Vec3 {
1054                x: 1.0,
1055                y: 1.0,
1056                z: 1.0
1057            }
1058        );
1059        assert_eq!(
1060            Vec3::unit_x(),
1061            Vec3 {
1062                x: 1.0,
1063                y: 0.0,
1064                z: 0.0
1065            }
1066        );
1067        assert_eq!(
1068            Vec3::unit_y(),
1069            Vec3 {
1070                x: 0.0,
1071                y: 1.0,
1072                z: 0.0
1073            }
1074        );
1075        assert_eq!(
1076            Vec3::unit_z(),
1077            Vec3 {
1078                x: 0.0,
1079                y: 0.0,
1080                z: 1.0
1081            }
1082        );
1083    }
1084
1085    #[test]
1086    fn test_vec3_cross() {
1087        let x = Vec3::unit_x();
1088        let y = Vec3::unit_y();
1089        let z = x.cross(y);
1090        assert!((z - Vec3::unit_z()).length() < 0.0001);
1091    }
1092
1093    #[test]
1094    fn test_vec3_operators() {
1095        let a = Vec3::new(1.0, 2.0, 3.0);
1096        let b = Vec3::new(4.0, 5.0, 6.0);
1097
1098        assert_eq!(a + b, Vec3::new(5.0, 7.0, 9.0));
1099        assert_eq!(a - b, Vec3::new(-3.0, -3.0, -3.0));
1100        assert_eq!(a * 2.0, Vec3::new(2.0, 4.0, 6.0));
1101        assert_eq!(2.0 * a, Vec3::new(2.0, 4.0, 6.0));
1102        assert_eq!(-a, Vec3::new(-1.0, -2.0, -3.0));
1103    }
1104
1105    #[test]
1106    fn test_vec3_cgmath_conversion() {
1107        let goud = Vec3::new(1.0, 2.0, 3.0);
1108        let cg: cgmath::Vector3<f32> = goud.into();
1109        assert_eq!(cg.x, 1.0);
1110        assert_eq!(cg.y, 2.0);
1111        assert_eq!(cg.z, 3.0);
1112
1113        let back: Vec3 = cg.into();
1114        assert_eq!(back, goud);
1115    }
1116
1117    #[test]
1118    fn test_vec3_dot() {
1119        let a = Vec3::new(1.0, 2.0, 3.0);
1120        let b = Vec3::new(4.0, 5.0, 6.0);
1121        // 1*4 + 2*5 + 3*6 = 4 + 10 + 18 = 32
1122        assert_eq!(a.dot(b), 32.0);
1123
1124        // Dot product with self is length squared
1125        assert_eq!(a.dot(a), a.length_squared());
1126
1127        // Dot product is commutative
1128        assert_eq!(a.dot(b), b.dot(a));
1129
1130        // Perpendicular vectors have dot product of 0
1131        let x = Vec3::unit_x();
1132        let y = Vec3::unit_y();
1133        assert_eq!(x.dot(y), 0.0);
1134    }
1135
1136    #[test]
1137    fn test_vec3_cross_properties() {
1138        let a = Vec3::new(1.0, 2.0, 3.0);
1139        let b = Vec3::new(4.0, 5.0, 6.0);
1140
1141        // Cross product is anti-commutative: a × b = -(b × a)
1142        let ab = a.cross(b);
1143        let ba = b.cross(a);
1144        assert!((ab + ba).length() < 0.0001);
1145
1146        // Cross product is perpendicular to both inputs
1147        assert!(ab.dot(a).abs() < 0.0001);
1148        assert!(ab.dot(b).abs() < 0.0001);
1149
1150        // Right-hand rule: x × y = z
1151        assert!((Vec3::unit_x().cross(Vec3::unit_y()) - Vec3::unit_z()).length() < 0.0001);
1152        assert!((Vec3::unit_y().cross(Vec3::unit_z()) - Vec3::unit_x()).length() < 0.0001);
1153        assert!((Vec3::unit_z().cross(Vec3::unit_x()) - Vec3::unit_y()).length() < 0.0001);
1154    }
1155
1156    #[test]
1157    fn test_vec3_length() {
1158        // 3-4-5 Pythagorean in 3D extended: sqrt(1 + 4 + 4) = 3
1159        let v = Vec3::new(1.0, 2.0, 2.0);
1160        assert_eq!(v.length_squared(), 9.0);
1161        assert_eq!(v.length(), 3.0);
1162
1163        // Zero vector has zero length
1164        assert_eq!(Vec3::zero().length(), 0.0);
1165
1166        // Unit vectors have length 1
1167        assert_eq!(Vec3::unit_x().length(), 1.0);
1168        assert_eq!(Vec3::unit_y().length(), 1.0);
1169        assert_eq!(Vec3::unit_z().length(), 1.0);
1170    }
1171
1172    #[test]
1173    fn test_vec3_normalize() {
1174        let v = Vec3::new(3.0, 4.0, 0.0);
1175        let n = v.normalize();
1176        assert!((n.length() - 1.0).abs() < 0.0001);
1177        assert!((n.x - 0.6).abs() < 0.0001);
1178        assert!((n.y - 0.8).abs() < 0.0001);
1179        assert_eq!(n.z, 0.0);
1180
1181        // Zero vector normalizes to zero (safe behavior)
1182        assert_eq!(Vec3::zero().normalize(), Vec3::zero());
1183
1184        // Unit vectors remain unchanged
1185        assert!((Vec3::unit_x().normalize() - Vec3::unit_x()).length() < 0.0001);
1186    }
1187
1188    #[test]
1189    fn test_vec3_lerp() {
1190        let a = Vec3::new(0.0, 0.0, 0.0);
1191        let b = Vec3::new(10.0, 20.0, 30.0);
1192
1193        // t=0 returns start
1194        assert_eq!(a.lerp(b, 0.0), a);
1195
1196        // t=1 returns end
1197        assert_eq!(a.lerp(b, 1.0), b);
1198
1199        // t=0.5 returns midpoint
1200        assert_eq!(a.lerp(b, 0.5), Vec3::new(5.0, 10.0, 15.0));
1201
1202        // Extrapolation works (t > 1)
1203        assert_eq!(a.lerp(b, 2.0), Vec3::new(20.0, 40.0, 60.0));
1204
1205        // Negative t extrapolates backwards
1206        assert_eq!(a.lerp(b, -0.5), Vec3::new(-5.0, -10.0, -15.0));
1207    }
1208
1209    #[test]
1210    fn test_vec3_ffi_layout() {
1211        use std::mem::{align_of, size_of};
1212
1213        // Verify Vec3 has expected FFI layout
1214        assert_eq!(size_of::<Vec3>(), 12); // 3 * f32 = 12 bytes
1215        assert_eq!(align_of::<Vec3>(), 4); // f32 alignment
1216
1217        // Verify fields are laid out consecutively with no padding
1218        let v = Vec3::new(1.0, 2.0, 3.0);
1219        let ptr = &v as *const Vec3 as *const f32;
1220        unsafe {
1221            assert_eq!(*ptr, 1.0); // x at offset 0
1222            assert_eq!(*ptr.add(1), 2.0); // y at offset 4
1223            assert_eq!(*ptr.add(2), 3.0); // z at offset 8
1224        }
1225
1226        // Verify Default trait
1227        assert_eq!(Vec3::default(), Vec3::zero());
1228    }
1229
1230    // =========================================================================
1231    // Vec4 Tests
1232    // =========================================================================
1233
1234    #[test]
1235    fn test_vec4_constructors() {
1236        assert_eq!(
1237            Vec4::new(1.0, 2.0, 3.0, 4.0),
1238            Vec4 {
1239                x: 1.0,
1240                y: 2.0,
1241                z: 3.0,
1242                w: 4.0
1243            }
1244        );
1245        assert_eq!(
1246            Vec4::zero(),
1247            Vec4 {
1248                x: 0.0,
1249                y: 0.0,
1250                z: 0.0,
1251                w: 0.0
1252            }
1253        );
1254        assert_eq!(
1255            Vec4::one(),
1256            Vec4 {
1257                x: 1.0,
1258                y: 1.0,
1259                z: 1.0,
1260                w: 1.0
1261            }
1262        );
1263    }
1264
1265    #[test]
1266    fn test_vec4_from_vec3() {
1267        let v3 = Vec3::new(1.0, 2.0, 3.0);
1268        let v4 = Vec4::from_vec3(v3, 4.0);
1269        assert_eq!(v4, Vec4::new(1.0, 2.0, 3.0, 4.0));
1270        assert_eq!(v4.xyz(), v3);
1271    }
1272
1273    #[test]
1274    fn test_vec4_cgmath_conversion() {
1275        let goud = Vec4::new(1.0, 2.0, 3.0, 4.0);
1276        let cg: cgmath::Vector4<f32> = goud.into();
1277        let back: Vec4 = cg.into();
1278        assert_eq!(back, goud);
1279    }
1280
1281    // =========================================================================
1282    // Rect Tests
1283    // =========================================================================
1284
1285    #[test]
1286    fn test_rect_constructors() {
1287        let r = Rect::new(10.0, 20.0, 100.0, 50.0);
1288        assert_eq!(r.x, 10.0);
1289        assert_eq!(r.y, 20.0);
1290        assert_eq!(r.width, 100.0);
1291        assert_eq!(r.height, 50.0);
1292    }
1293
1294    #[test]
1295    fn test_rect_from_min_max() {
1296        let r = Rect::from_min_max(Vec2::new(10.0, 20.0), Vec2::new(110.0, 70.0));
1297        assert_eq!(r.x, 10.0);
1298        assert_eq!(r.y, 20.0);
1299        assert_eq!(r.width, 100.0);
1300        assert_eq!(r.height, 50.0);
1301    }
1302
1303    #[test]
1304    fn test_rect_accessors() {
1305        let r = Rect::new(10.0, 20.0, 100.0, 50.0);
1306        assert_eq!(r.min(), Vec2::new(10.0, 20.0));
1307        assert_eq!(r.max(), Vec2::new(110.0, 70.0));
1308        assert_eq!(r.center(), Vec2::new(60.0, 45.0));
1309        assert_eq!(r.size(), Vec2::new(100.0, 50.0));
1310        assert_eq!(r.area(), 5000.0);
1311    }
1312
1313    #[test]
1314    fn test_rect_contains() {
1315        let r = Rect::new(0.0, 0.0, 100.0, 100.0);
1316        assert!(r.contains(Vec2::new(50.0, 50.0)));
1317        assert!(r.contains(Vec2::new(0.0, 0.0)));
1318        assert!(!r.contains(Vec2::new(100.0, 100.0))); // exclusive max
1319        assert!(!r.contains(Vec2::new(-1.0, 50.0)));
1320        assert!(!r.contains(Vec2::new(50.0, -1.0)));
1321    }
1322
1323    #[test]
1324    fn test_rect_intersects() {
1325        let a = Rect::new(0.0, 0.0, 100.0, 100.0);
1326        let b = Rect::new(50.0, 50.0, 100.0, 100.0);
1327        let c = Rect::new(200.0, 200.0, 10.0, 10.0);
1328
1329        assert!(a.intersects(&b));
1330        assert!(b.intersects(&a));
1331        assert!(!a.intersects(&c));
1332        assert!(!c.intersects(&a));
1333    }
1334
1335    #[test]
1336    fn test_rect_intersection() {
1337        let a = Rect::new(0.0, 0.0, 100.0, 100.0);
1338        let b = Rect::new(50.0, 50.0, 100.0, 100.0);
1339
1340        let inter = a.intersection(&b).unwrap();
1341        assert_eq!(inter.x, 50.0);
1342        assert_eq!(inter.y, 50.0);
1343        assert_eq!(inter.width, 50.0);
1344        assert_eq!(inter.height, 50.0);
1345
1346        let c = Rect::new(200.0, 200.0, 10.0, 10.0);
1347        assert!(a.intersection(&c).is_none());
1348    }
1349
1350    // =========================================================================
1351    // Color Tests
1352    // =========================================================================
1353
1354    #[test]
1355    fn test_color_constants() {
1356        assert_eq!(Color::WHITE, Color::rgba(1.0, 1.0, 1.0, 1.0));
1357        assert_eq!(Color::BLACK, Color::rgba(0.0, 0.0, 0.0, 1.0));
1358        assert_eq!(Color::RED, Color::rgba(1.0, 0.0, 0.0, 1.0));
1359        assert_eq!(Color::TRANSPARENT, Color::rgba(0.0, 0.0, 0.0, 0.0));
1360    }
1361
1362    #[test]
1363    fn test_color_from_u8() {
1364        let c = Color::from_u8(255, 128, 0, 255);
1365        assert!((c.r - 1.0).abs() < 0.01);
1366        assert!((c.g - 0.5).abs() < 0.01);
1367        assert_eq!(c.b, 0.0);
1368        assert_eq!(c.a, 1.0);
1369    }
1370
1371    #[test]
1372    fn test_color_from_hex() {
1373        let c1 = Color::from_hex(0xFF0000); // Red
1374        assert_eq!(c1.r, 1.0);
1375        assert_eq!(c1.g, 0.0);
1376        assert_eq!(c1.b, 0.0);
1377        assert_eq!(c1.a, 1.0);
1378
1379        let c2 = Color::from_hex(0xFF000080); // Red with 50% alpha
1380        assert_eq!(c2.r, 1.0);
1381        assert_eq!(c2.g, 0.0);
1382        assert_eq!(c2.b, 0.0);
1383        assert!((c2.a - 0.5).abs() < 0.01);
1384    }
1385
1386    #[test]
1387    fn test_color_vec_conversions() {
1388        let c = Color::rgba(0.1, 0.2, 0.3, 0.4);
1389        let v3 = c.to_vec3();
1390        assert_eq!(v3, Vec3::new(0.1, 0.2, 0.3));
1391
1392        let v4 = c.to_vec4();
1393        assert_eq!(v4, Vec4::new(0.1, 0.2, 0.3, 0.4));
1394
1395        let c2 = Color::from_vec4(v4);
1396        assert_eq!(c2, c);
1397    }
1398
1399    #[test]
1400    fn test_color_lerp() {
1401        let a = Color::BLACK;
1402        let b = Color::WHITE;
1403        let mid = a.lerp(b, 0.5);
1404        assert!((mid.r - 0.5).abs() < 0.0001);
1405        assert!((mid.g - 0.5).abs() < 0.0001);
1406        assert!((mid.b - 0.5).abs() < 0.0001);
1407    }
1408
1409    #[test]
1410    fn test_color_with_alpha() {
1411        let c = Color::RED.with_alpha(0.5);
1412        assert_eq!(c.r, 1.0);
1413        assert_eq!(c.g, 0.0);
1414        assert_eq!(c.b, 0.0);
1415        assert_eq!(c.a, 0.5);
1416    }
1417
1418    // =========================================================================
1419    // FFI Layout Tests
1420    // =========================================================================
1421
1422    #[test]
1423    fn test_ffi_layout_sizes() {
1424        use std::mem::size_of;
1425
1426        // Verify types have expected sizes for FFI
1427        assert_eq!(size_of::<Vec2>(), 8); // 2 * f32
1428        assert_eq!(size_of::<Vec3>(), 12); // 3 * f32
1429        assert_eq!(size_of::<Vec4>(), 16); // 4 * f32
1430        assert_eq!(size_of::<Rect>(), 16); // 4 * f32
1431        assert_eq!(size_of::<Color>(), 16); // 4 * f32
1432    }
1433}