1#[derive(Debug, Copy, Clone, PartialEq, Default)]
4#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
5pub struct Color {
6 pub r: f32,
7 pub g: f32,
8 pub b: f32,
9}
10
11impl Color {
12 pub const BLACK: Color = Color {
13 r: 0.0,
14 g: 0.0,
15 b: 0.0,
16 };
17 pub const WHITE: Color = Color {
18 r: 1.0,
19 g: 1.0,
20 b: 1.0,
21 };
22 pub const RED: Color = Color {
23 r: 1.0,
24 g: 0.0,
25 b: 0.0,
26 };
27 pub const GREEN: Color = Color {
28 r: 0.0,
29 g: 1.0,
30 b: 0.0,
31 };
32 pub const BLUE: Color = Color {
33 r: 0.0,
34 g: 0.0,
35 b: 1.0,
36 };
37 pub const CYAN: Color = Color {
38 r: 0.0,
39 g: 1.0,
40 b: 1.0,
41 };
42 pub const MAGENTA: Color = Color {
43 r: 1.0,
44 g: 0.0,
45 b: 1.0,
46 };
47 pub const YELLOW: Color = Color {
48 r: 1.0,
49 g: 1.0,
50 b: 0.0,
51 };
52
53 pub fn new(r: f32, g: f32, b: f32) -> Self {
62 Self { r, g, b }
63 }
64
65 pub fn from_hue(hue: f32) -> Self {
82 match hue * 6.0 {
83 hue if (0.0..1.0).contains(&hue) => Self::new(1.0, hue, 0.0), hue if (1.0..2.0).contains(&hue) => Self::new(2.0 - hue, 1.0, 0.0), hue if (2.0..3.0).contains(&hue) => Self::new(0.0, 1.0, hue - 2.0), hue if (3.0..4.0).contains(&hue) => Self::new(0.0, 4.0 - hue, 1.0), hue if (4.0..5.0).contains(&hue) => Self::new(hue - 4.0, 0.0, 1.0), hue if (5.0..6.0).contains(&hue) => Self::new(1.0, 0.0, 6.0 - hue), _ => {
90 let hue = hue.fract();
92 let hue = if hue < 0.0 { 1.0 + hue } else { hue };
93 Self::from_hue(hue)
94 }
95 }
96 }
97
98 pub fn red_green_color(hue: f32) -> Self {
110 let a = |x| {
111 if x < 0.25 {
112 4.0 * x
113 } else if x >= 0.75 {
114 4.0 - 4.0 * x
115 } else {
116 1.0
117 }
118 };
119
120 let r = a(hue.fract());
121 let g = a((hue + 0.5).fract());
122 Self::new(r, g, 0.0)
123 }
124
125 pub fn clamp(self) -> Self {
140 let Self { r, g, b } = self;
141
142 let highest_component = r.max(g).max(b);
143 let multiplier = if highest_component > 1.0 {
144 1.0 / highest_component
145 } else {
146 1.0
147 };
148
149 let r = f32::clamp(r * multiplier, 0.0, 1.0);
150 let g = f32::clamp(g * multiplier, 0.0, 1.0);
151 let b = f32::clamp(b * multiplier, 0.0, 1.0);
152
153 Self { r, g, b }
154 }
155
156 pub fn quantize(self, range: u8) -> (u8, u8, u8) {
165 let Self { r, g, b } = self.clamp();
166
167 let quantize_component = |c| u8::min((c * range as f32) as u8, range - 1);
168 (
169 quantize_component(r),
170 quantize_component(g),
171 quantize_component(b),
172 )
173 }
174
175 pub fn mix(self, other: Color, proportion_of_other: f32) -> Color {
186 other * proportion_of_other + self * (1.0 - proportion_of_other)
187 }
188}
189
190impl std::cmp::Eq for Color {}
191
192impl std::ops::Mul<f32> for Color {
193 type Output = Self;
194
195 fn mul(self, multiplier: f32) -> Self::Output {
196 Self {
197 r: self.r * multiplier,
198 g: self.g * multiplier,
199 b: self.b * multiplier,
200 }
201 }
202}
203
204impl std::ops::Div<f32> for Color {
205 type Output = Self;
206
207 fn div(self, multiplier: f32) -> Self::Output {
208 Self {
209 r: self.r / multiplier,
210 g: self.g / multiplier,
211 b: self.b / multiplier,
212 }
213 }
214}
215
216impl std::ops::Add for Color {
217 type Output = Self;
218
219 fn add(self, other: Self) -> Self {
220 Self {
221 r: self.r + other.r,
222 g: self.g + other.g,
223 b: self.b + other.b,
224 }
225 }
226}
227
228impl std::ops::Sub for Color {
229 type Output = Self;
230
231 fn sub(self, other: Self) -> Self {
232 Self {
233 r: self.r - other.r,
234 g: self.g - other.g,
235 b: self.b - other.b,
236 }
237 }
238}
239
240impl std::ops::Add<f32> for Color {
241 type Output = Self;
242
243 fn add(self, addend: f32) -> Self {
244 Self {
245 r: self.r + addend,
246 g: self.g + addend,
247 b: self.b + addend,
248 }
249 }
250}
251
252impl std::ops::Sub<f32> for Color {
253 type Output = Self;
254
255 fn sub(self, subtrand : f32) -> Self {
256 Self {
257 r: self.r - subtrand,
258 g: self.g - subtrand,
259 b: self.b - subtrand,
260 }
261 }
262}
263
264impl std::ops::Neg for Color {
265 type Output = Self;
266
267 fn neg(self) -> Self {
268 Self {
269 r: -self.r,
270 g: -self.g,
271 b: -self.b,
272 }
273 }
274}
275
276impl std::iter::Sum for Color {
277 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
278 iter.fold(Color::BLACK, |a, b| a + b)
279 }
280}
281
282#[cfg(feature = "embedded-graphics")]
283impl From<Color> for embedded_graphics::pixelcolor::Rgb888 {
284 fn from(color: Color) -> Self {
285 let (r, g, b) = color.quantize(255);
286 Self::new(r, g, b)
287 }
288}
289
290#[cfg(feature = "embedded-graphics")]
291impl From<embedded_graphics::pixelcolor::Rgb888> for Color {
292 fn from(color: embedded_graphics::pixelcolor::Rgb888) -> Self {
293 use embedded_graphics::pixelcolor::RgbColor;
294
295 Color::new(
296 (color.r() as f32 + 0.5) / 256.0,
297 (color.g() as f32 + 0.5) / 256.0,
298 (color.b() as f32 + 0.5) / 256.0,
299 )
300 }
301}