iced_aw/core/
color.rs

1//! Helper functions and structs for picking dates.
2
3use iced_core::Color;
4
5/// A color in the HSV color space.
6#[derive(Clone, Copy, Debug, PartialEq)]
7pub struct Hsv {
8    /// HSV hue.
9    pub(crate) hue: u16,
10    /// HSV Saturation.
11    pub(crate) saturation: f32,
12    /// HSV value.
13    pub(crate) value: f32,
14}
15
16impl Hsv {
17    /// Creates a [`Hsv`] from its HSV components.
18    #[must_use]
19    pub const fn from_hsv(hue: u16, saturation: f32, value: f32) -> Self {
20        Self {
21            hue,
22            saturation,
23            value,
24        }
25    }
26}
27
28/// Creates a string of hexadecimal characters.
29pub trait HexString {
30    /// Turns self into a string of hexadecimal characters.
31    fn as_hex_string(&self) -> String;
32}
33
34impl HexString for Color {
35    fn as_hex_string(&self) -> String {
36        format!(
37            "#{:02X?}{:02X?}{:02X?}{:02X?}",
38            (self.r * 255.0) as u8,
39            (self.g * 255.0) as u8,
40            (self.b * 255.0) as u8,
41            (self.a * 255.0) as u8,
42        )
43    }
44}
45
46impl From<Color> for Hsv {
47    // https://de.wikipedia.org/wiki/HSV-Farbraum#Umrechnung_RGB_in_HSV/HSL
48    fn from(color: Color) -> Self {
49        let max = color.r.max(color.g.max(color.b));
50        let min = color.r.min(color.g.min(color.b));
51
52        let hue = if (max - min).abs() < f32::EPSILON {
53            0.0
54        } else if (max - color.r).abs() < f32::EPSILON {
55            60.0 * (0.0 + (color.g - color.b) / (max - min))
56        } else if (max - color.g).abs() < f32::EPSILON {
57            60.0 * (2.0 + (color.b - color.r) / (max - min))
58        } else {
59            60.0 * (4.0 + (color.r - color.g) / (max - min))
60        };
61
62        let hue = if hue < 0.0 { hue + 360.0 } else { hue } as u16 % 360;
63
64        let saturation = if max == 0.0 { 0.0 } else { (max - min) / max };
65
66        let value = max;
67
68        Self {
69            hue,
70            saturation,
71            value,
72        }
73    }
74}
75
76impl From<Hsv> for Color {
77    fn from(hsv: Hsv) -> Self {
78        // https://de.wikipedia.org/wiki/HSV-Farbraum#Umrechnung_HSV_in_RGB
79        let h_i = (f32::from(hsv.hue) / 60.0).floor();
80        let f = (f32::from(hsv.hue) / 60.0) - h_i;
81
82        let p = hsv.value * (1.0 - hsv.saturation);
83        let q = hsv.value * (1.0 - hsv.saturation * f);
84        let t = hsv.value * (1.0 - hsv.saturation * (1.0 - f));
85
86        let h_i = h_i as u8;
87        let (red, green, blue) = match h_i {
88            1 => (q, hsv.value, p),
89            2 => (p, hsv.value, t),
90            3 => (p, q, hsv.value),
91            4 => (t, p, hsv.value),
92            5 => (hsv.value, p, q),
93            _ => (hsv.value, t, p),
94        };
95
96        Self::from_rgb(red, green, blue)
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use iced_core::Color;
103
104    use super::Hsv;
105
106    #[allow(clippy::cognitive_complexity)]
107    #[test]
108    fn rgb_to_hsv() {
109        // https://de.wikipedia.org/wiki/HSV-Farbraum#Transformation_von_HSV/HSL_und_RGB
110        let red_rgb = Color::from_rgb(1.0, 0.0, 0.0);
111        let red_hsv = Hsv::from_hsv(0, 1.0, 1.0);
112        assert_eq!(red_hsv, red_rgb.into());
113
114        let orange_rgb = Color::from_rgb(1.0, 0.5, 0.0);
115        let orange_hsv = Hsv::from_hsv(30, 1.0, 1.0);
116        assert_eq!(orange_hsv, orange_rgb.into());
117
118        let yellow_rgb = Color::from_rgb(1.0, 1.0, 0.0);
119        let yellow_hsv = Hsv::from_hsv(60, 1.0, 1.0);
120        assert_eq!(yellow_hsv, yellow_rgb.into());
121
122        let dark_green_rgb = Color::from_rgb(0.0, 0.5, 0.0);
123        let dark_green_hsv = Hsv::from_hsv(120, 1.0, 0.5);
124        assert_eq!(dark_green_hsv, dark_green_rgb.into());
125
126        let violett_rgb = Color::from_rgb(0.5, 0.0, 1.0);
127        let violett_hsv = Hsv::from_hsv(270, 1.0, 1.0);
128        assert_eq!(violett_hsv, violett_rgb.into());
129
130        let black_rgb = Color::from_rgb(0.0, 0.0, 0.0);
131        let black_hsv = Hsv::from_hsv(0, 0.0, 0.0);
132        assert_eq!(black_hsv, black_rgb.into());
133
134        let blue_rgb = Color::from_rgb(0.0, 0.0, 1.0);
135        let blue_hsv = Hsv::from_hsv(240, 1.0, 1.0);
136        assert_eq!(blue_hsv, blue_rgb.into());
137
138        let brown_rgb = Color::from_rgb(0.36, 0.18, 0.09);
139        let brown_hsv = Hsv::from_hsv(20, 0.75, 0.36);
140        assert_eq!(brown_hsv, brown_rgb.into());
141
142        let white_rgb = Color::from_rgb(1.0, 1.0, 1.0);
143        let white_hsv = Hsv::from_hsv(0, 0.0, 1.0);
144        assert_eq!(white_hsv, white_rgb.into());
145
146        let green_rgb = Color::from_rgb(0.0, 1.0, 0.0);
147        let green_hsv = Hsv::from_hsv(120, 1.0, 1.0);
148        assert_eq!(green_hsv, green_rgb.into());
149
150        let cyan_rgb = Color::from_rgb(0.0, 1.0, 1.0);
151        let cyan_hsv = Hsv::from_hsv(180, 1.0, 1.0);
152        assert_eq!(cyan_hsv, cyan_rgb.into());
153
154        let magenta_rgb = Color::from_rgb(1.0, 0.0, 1.0);
155        let magenta_hsv = Hsv::from_hsv(300, 1.0, 1.0);
156        assert_eq!(magenta_hsv, magenta_rgb.into());
157
158        let blue_green_rgb = Color::from_rgb(0.0, 1.0, 0.5);
159        let blue_green_hsv = Hsv::from_hsv(150, 1.0, 1.0);
160        assert_eq!(blue_green_hsv, blue_green_rgb.into());
161
162        let green_blue_rgb = Color::from_rgb(0.0, 0.5, 1.0);
163        let green_blue_hsv = Hsv::from_hsv(210, 1.0, 1.0);
164        assert_eq!(green_blue_hsv, green_blue_rgb.into());
165
166        let green_yellow_rgb = Color::from_rgb(0.5, 1.0, 0.0);
167        let green_yellow_hsv = Hsv::from_hsv(90, 1.0, 1.0);
168        assert_eq!(green_yellow_hsv, green_yellow_rgb.into());
169
170        let blue_red_rgb = Color::from_rgb(1.0, 0.0, 0.5);
171        let blue_red_hsv = Hsv::from_hsv(330, 1.0, 1.0);
172        assert_eq!(blue_red_hsv, blue_red_rgb.into());
173
174        let zinnober_rgb = Color::from_rgb(1.0, 0.25, 0.0);
175        let zinnober_hsv = Hsv::from_hsv(15, 1.0, 1.0);
176        assert_eq!(zinnober_hsv, zinnober_rgb.into());
177
178        let indigo_rgb = Color::from_rgb(0.25, 0.0, 1.0);
179        let indigo_hsv = Hsv::from_hsv(255, 1.0, 1.0);
180        assert_eq!(indigo_hsv, indigo_rgb.into());
181
182        let light_blue_green_rgb = Color::from_rgb(0.0, 1.0, 0.25);
183        let light_blue_green_hsv = Hsv::from_hsv(135, 1.0, 1.0);
184        assert_eq!(light_blue_green_hsv, light_blue_green_rgb.into());
185
186        let blue_cyan_rgb = Color::from_rgb(0.0, 0.75, 1.0);
187        let blue_cyan_hsv = Hsv::from_hsv(195, 1.0, 1.0);
188        assert_eq!(blue_cyan_hsv, blue_cyan_rgb.into());
189
190        let light_green_yellow_rgb = Color::from_rgb(0.75, 1.0, 0.0);
191        let light_green_yellow_hsv = Hsv::from_hsv(75, 1.0, 1.0);
192        assert_eq!(light_green_yellow_hsv, light_green_yellow_rgb.into());
193
194        let red_magenta_rgb = Color::from_rgb(1.0, 0.0, 0.75);
195        let red_magenta_hsv = Hsv::from_hsv(315, 1.0, 1.0);
196        assert_eq!(red_magenta_hsv, red_magenta_rgb.into());
197
198        let safran_rgb = Color::from_rgb(1.0, 0.75, 0.0);
199        let safran_hsv = Hsv::from_hsv(45, 1.0, 1.0);
200        assert_eq!(safran_hsv, safran_rgb.into());
201
202        let blue_magenta_rgb = Color::from_rgb(0.75, 0.0, 1.0);
203        let blue_magenta_hsv = Hsv::from_hsv(285, 1.0, 1.0);
204        assert_eq!(blue_magenta_hsv, blue_magenta_rgb.into());
205
206        let green_cyan_rgb = Color::from_rgb(0.0, 1.0, 0.75);
207        let green_cyan_hsv = Hsv::from_hsv(165, 1.0, 1.0);
208        assert_eq!(green_cyan_hsv, green_cyan_rgb.into());
209
210        let light_green_blue_rgb = Color::from_rgb(0.0, 0.25, 1.0);
211        let light_green_blue_hsv = Hsv::from_hsv(225, 1.0, 1.0);
212        assert_eq!(light_green_blue_hsv, light_green_blue_rgb.into());
213
214        let lime_rgb = Color::from_rgb(0.25, 1.0, 0.0);
215        let lime_hsv = Hsv::from_hsv(105, 1.0, 1.0);
216        assert_eq!(lime_hsv, lime_rgb.into());
217
218        let light_blue_red_rgb = Color::from_rgb(1.0, 0.0, 0.25);
219        let light_blue_red_hsv = Hsv::from_hsv(345, 1.0, 1.0);
220        assert_eq!(light_blue_red_hsv, light_blue_red_rgb.into());
221    }
222
223    #[allow(clippy::cognitive_complexity)]
224    #[test]
225    fn hsv_to_rgb() {
226        // https://de.wikipedia.org/wiki/HSV-Farbraum#Transformation_von_HSV/HSL_und_RGB
227        let red_hsv = Hsv::from_hsv(0, 1.0, 1.0);
228        let red_rgb = Color::from_rgb(1.0, 0.0, 0.0);
229        assert_eq!(red_rgb, red_hsv.into());
230
231        let orange_hsv = Hsv::from_hsv(30, 1.0, 1.0);
232        let orange_rgb = Color::from_rgb(1.0, 0.5, 0.0);
233        assert_eq!(orange_rgb, orange_hsv.into());
234
235        let yellow_hsv = Hsv::from_hsv(60, 1.0, 1.0);
236        let yellow_rgb = Color::from_rgb(1.0, 1.0, 0.0);
237        assert_eq!(yellow_rgb, yellow_hsv.into());
238
239        let dark_green_hsv = Hsv::from_hsv(120, 1.0, 0.5);
240        let dark_green_rgb = Color::from_rgb(0.0, 0.5, 0.0);
241        assert_eq!(dark_green_rgb, dark_green_hsv.into());
242
243        let violett_hsv = Hsv::from_hsv(270, 1.0, 1.0);
244        let violett_rgb = Color::from_rgb(0.5, 0.0, 1.0);
245        assert_eq!(violett_rgb, violett_hsv.into());
246
247        let black_hsv = Hsv::from_hsv(0, 0.0, 0.0);
248        let black_rgb = Color::from_rgb(0.0, 0.0, 0.0);
249        assert_eq!(black_rgb, black_hsv.into());
250
251        let blue_hsv = Hsv::from_hsv(240, 1.0, 1.0);
252        let blue_rgb = Color::from_rgb(0.0, 0.0, 1.0);
253        assert_eq!(blue_rgb, blue_hsv.into());
254
255        let brown_hsv = Hsv::from_hsv(20, 0.75, 0.36);
256        let brown_rgb = Color::from_rgb(0.36, 0.18, 0.09);
257        assert_eq!(brown_rgb, brown_hsv.into());
258
259        let white_hsv = Hsv::from_hsv(0, 0.0, 1.0);
260        let white_rgb = Color::from_rgb(1.0, 1.0, 1.0);
261        assert_eq!(white_rgb, white_hsv.into());
262
263        let green_hsv = Hsv::from_hsv(120, 1.0, 1.0);
264        let green_rgb = Color::from_rgb(0.0, 1.0, 0.0);
265        assert_eq!(green_rgb, green_hsv.into());
266
267        let cyan_hsv = Hsv::from_hsv(180, 1.0, 1.0);
268        let cyan_rgb = Color::from_rgb(0.0, 1.0, 1.0);
269        assert_eq!(cyan_rgb, cyan_hsv.into());
270
271        let magenta_hsv = Hsv::from_hsv(300, 1.0, 1.0);
272        let magenta_rgb = Color::from_rgb(1.0, 0.0, 1.0);
273        assert_eq!(magenta_rgb, magenta_hsv.into());
274
275        let blue_green_hsv = Hsv::from_hsv(150, 1.0, 1.0);
276        let blue_green_rgb = Color::from_rgb(0.0, 1.0, 0.5);
277        assert_eq!(blue_green_rgb, blue_green_hsv.into());
278
279        let green_blue_hsv = Hsv::from_hsv(210, 1.0, 1.0);
280        let green_blue_rgb = Color::from_rgb(0.0, 0.5, 1.0);
281        assert_eq!(green_blue_rgb, green_blue_hsv.into());
282
283        let green_yellow_hsv = Hsv::from_hsv(90, 1.0, 1.0);
284        let green_yellow_rgb = Color::from_rgb(0.5, 1.0, 0.0);
285        assert_eq!(green_yellow_rgb, green_yellow_hsv.into());
286
287        let blue_red_hsv = Hsv::from_hsv(330, 1.0, 1.0);
288        let blue_red_rgb = Color::from_rgb(1.0, 0.0, 0.5);
289        assert_eq!(blue_red_rgb, blue_red_hsv.into());
290
291        let zinnober_hsv = Hsv::from_hsv(15, 1.0, 1.0);
292        let zinnober_rgb = Color::from_rgb(1.0, 0.25, 0.0);
293        assert_eq!(zinnober_rgb, zinnober_hsv.into());
294
295        let indigo_hsv = Hsv::from_hsv(255, 1.0, 1.0);
296        let indigo_rgb = Color::from_rgb(0.25, 0.0, 1.0);
297        assert_eq!(indigo_rgb, indigo_hsv.into());
298
299        let light_blue_green_hsv = Hsv::from_hsv(135, 1.0, 1.0);
300        let light_blue_green_rgb = Color::from_rgb(0.0, 1.0, 0.25);
301        assert_eq!(light_blue_green_rgb, light_blue_green_hsv.into());
302
303        let blue_cyan_hsv = Hsv::from_hsv(195, 1.0, 1.0);
304        let blue_cyan_rgb = Color::from_rgb(0.0, 0.75, 1.0);
305        assert_eq!(blue_cyan_rgb, blue_cyan_hsv.into());
306
307        let light_green_yellow_hsv = Hsv::from_hsv(75, 1.0, 1.0);
308        let light_green_yellow_rgb = Color::from_rgb(0.75, 1.0, 0.0);
309        assert_eq!(light_green_yellow_rgb, light_green_yellow_hsv.into());
310
311        let red_magenta_hsv = Hsv::from_hsv(315, 1.0, 1.0);
312        let red_magenta_rgb = Color::from_rgb(1.0, 0.0, 0.75);
313        assert_eq!(red_magenta_rgb, red_magenta_hsv.into());
314
315        let safran_hsv = Hsv::from_hsv(45, 1.0, 1.0);
316        let safran_rgb = Color::from_rgb(1.0, 0.75, 0.0);
317        assert_eq!(safran_rgb, safran_hsv.into());
318
319        let blue_magenta_hsv = Hsv::from_hsv(285, 1.0, 1.0);
320        let blue_magenta_rgb = Color::from_rgb(0.75, 0.0, 1.0);
321        assert_eq!(blue_magenta_rgb, blue_magenta_hsv.into());
322
323        let green_cyan_hsv = Hsv::from_hsv(165, 1.0, 1.0);
324        let green_cyan_rgb = Color::from_rgb(0.0, 1.0, 0.75);
325        assert_eq!(green_cyan_rgb, green_cyan_hsv.into());
326
327        let light_green_blue_hsv = Hsv::from_hsv(225, 1.0, 1.0);
328        let light_green_blue_rgb = Color::from_rgb(0.0, 0.25, 1.0);
329        assert_eq!(light_green_blue_rgb, light_green_blue_hsv.into());
330
331        let lime_hsv = Hsv::from_hsv(105, 1.0, 1.0);
332        let lime_rgb = Color::from_rgb(0.25, 1.0, 0.0);
333        assert_eq!(lime_rgb, lime_hsv.into());
334
335        let light_blue_red_hsv = Hsv::from_hsv(345, 1.0, 1.0);
336        let light_blue_red_rgb = Color::from_rgb(1.0, 0.0, 0.25);
337        assert_eq!(light_blue_red_rgb, light_blue_red_hsv.into());
338    }
339}