1#[derive(Debug, Clone, Copy, PartialEq)]
5pub struct Color {
6 pub r: f32,
7 pub g: f32,
8 pub b: f32,
9 pub a: f32,
10}
11
12impl Color {
13 pub const fn rgb(r: f32, g: f32, b: f32) -> Self {
15 Self { r, g, b, a: 1.0 }
16 }
17
18 pub const fn rgba(r: f32, g: f32, b: f32, a: f32) -> Self {
20 Self { r, g, b, a }
21 }
22
23 pub fn from_hex(hex: u32) -> Self {
25 let r = ((hex >> 16) & 0xFF) as f32 / 255.0;
26 let g = ((hex >> 8) & 0xFF) as f32 / 255.0;
27 let b = (hex & 0xFF) as f32 / 255.0;
28 Self::rgb(r, g, b)
29 }
30
31 pub fn with_alpha(self, a: f32) -> Self {
33 Self { a, ..self }
34 }
35
36 pub fn lerp(self, other: Self, t: f32) -> Self {
38 Self {
39 r: self.r + (other.r - self.r) * t,
40 g: self.g + (other.g - self.g) * t,
41 b: self.b + (other.b - self.b) * t,
42 a: self.a + (other.a - self.a) * t,
43 }
44 }
45
46 pub const WHITE: Self = Self::rgb(1.0, 1.0, 1.0);
48 pub const BLACK: Self = Self::rgb(0.0, 0.0, 0.0);
50 pub const TRANSPARENT: Self = Self::rgba(0.0, 0.0, 0.0, 0.0);
52}
53
54#[derive(Debug, Clone)]
56pub struct ColorScheme {
57 pub primary: Color,
58 pub on_primary: Color,
59 pub primary_container: Color,
60 pub on_primary_container: Color,
61 pub secondary: Color,
62 pub on_secondary: Color,
63 pub surface: Color,
64 pub on_surface: Color,
65 pub surface_variant: Color,
66 pub background: Color,
67 pub on_background: Color,
68 pub error: Color,
69 pub on_error: Color,
70 pub outline: Color,
71 pub shadow: Color,
72}
73
74#[cfg(test)]
79mod tests {
80 use super::*;
81
82 #[test]
83 fn color_rgb_sets_alpha_to_one() {
84 let c = Color::rgb(0.5, 0.5, 0.5);
85 assert_eq!(c.a, 1.0);
86 }
87
88 #[test]
89 fn color_from_hex_parses_correctly() {
90 let c = Color::from_hex(0x6750A4);
92 let expected_r = 103.0_f32 / 255.0;
93 let expected_g = 80.0_f32 / 255.0;
94 let expected_b = 164.0_f32 / 255.0;
95 assert!((c.r - expected_r).abs() < 1e-5, "r mismatch: {}", c.r);
96 assert!((c.g - expected_g).abs() < 1e-5, "g mismatch: {}", c.g);
97 assert!((c.b - expected_b).abs() < 1e-5, "b mismatch: {}", c.b);
98 assert_eq!(c.a, 1.0);
99 }
100
101 #[test]
102 fn color_from_hex_white() {
103 let c = Color::from_hex(0xFFFFFF);
104 assert!((c.r - 1.0).abs() < 1e-5);
105 assert!((c.g - 1.0).abs() < 1e-5);
106 assert!((c.b - 1.0).abs() < 1e-5);
107 }
108
109 #[test]
110 fn color_lerp_midpoint() {
111 let a = Color::rgb(0.0, 0.0, 0.0);
112 let b = Color::rgb(1.0, 1.0, 1.0);
113 let mid = a.lerp(b, 0.5);
114 assert!((mid.r - 0.5).abs() < 1e-6);
115 assert!((mid.g - 0.5).abs() < 1e-6);
116 assert!((mid.b - 0.5).abs() < 1e-6);
117 assert!((mid.a - 1.0).abs() < 1e-6);
118 }
119
120 #[test]
121 fn color_lerp_t0_returns_self() {
122 let a = Color::rgb(0.2, 0.4, 0.6);
123 let b = Color::rgb(1.0, 1.0, 1.0);
124 let result = a.lerp(b, 0.0);
125 assert!((result.r - a.r).abs() < 1e-6);
126 assert!((result.g - a.g).abs() < 1e-6);
127 assert!((result.b - a.b).abs() < 1e-6);
128 }
129
130 #[test]
131 fn color_lerp_t1_returns_other() {
132 let a = Color::rgb(0.0, 0.0, 0.0);
133 let b = Color::rgb(0.3, 0.6, 0.9);
134 let result = a.lerp(b, 1.0);
135 assert!((result.r - b.r).abs() < 1e-6);
136 assert!((result.g - b.g).abs() < 1e-6);
137 assert!((result.b - b.b).abs() < 1e-6);
138 }
139
140 #[test]
141 fn color_with_alpha() {
142 let c = Color::WHITE.with_alpha(0.5);
143 assert_eq!(c.r, 1.0);
144 assert_eq!(c.g, 1.0);
145 assert_eq!(c.b, 1.0);
146 assert!((c.a - 0.5).abs() < 1e-6);
147 }
148
149 #[test]
150 fn color_constants() {
151 assert_eq!(Color::WHITE.a, 1.0);
152 assert_eq!(Color::BLACK.r, 0.0);
153 assert_eq!(Color::TRANSPARENT.a, 0.0);
154 }
155}