1use bevy::color::Color;
2
3#[derive(Clone, Debug, PartialEq)]
4pub enum ScalarColorMap {
5 Viridis,
6 Inferno,
7 Plasma,
8 Sampled(Vec<[f32; 4]>),
9}
10
11impl ScalarColorMap {
12 pub fn color(&self, value: f32) -> Color {
13 match self {
14 Self::Viridis => sample_stops(value, &viridis_stops()),
15 Self::Inferno => sample_stops(value, &inferno_stops()),
16 Self::Plasma => sample_stops(value, &plasma_stops()),
17 Self::Sampled(colors) => sample_colors(value, colors),
18 }
19 }
20}
21
22fn sample_stops(value: f32, stops: &[(f32, [f32; 4])]) -> Color {
23 let clamped = value.clamp(0.0, 1.0);
24 for window in stops.windows(2) {
25 let (start_t, start) = window[0];
26 let (end_t, end) = window[1];
27 if clamped <= end_t {
28 let local = (clamped - start_t) / (end_t - start_t);
29 return lerp_color(start, end, local);
30 }
31 }
32
33 let [r, g, b, a] = stops.last().map(|(_, color)| *color).unwrap_or([1.0; 4]);
34 Color::srgba(r, g, b, a)
35}
36
37fn sample_colors(value: f32, colors: &[[f32; 4]]) -> Color {
38 match colors.len() {
39 0 => Color::WHITE,
40 1 => {
41 let [r, g, b, a] = colors[0];
42 Color::srgba(r, g, b, a)
43 }
44 len => {
45 let position = value.clamp(0.0, 1.0) * (len - 1) as f32;
46 let lower = position.floor() as usize;
47 let upper = position.ceil() as usize;
48 let local = position - lower as f32;
49 lerp_color(colors[lower], colors[upper], local)
50 }
51 }
52}
53
54fn lerp_color(start: [f32; 4], end: [f32; 4], t: f32) -> Color {
55 Color::srgba(
56 (1.0 - t) * start[0] + t * end[0],
57 (1.0 - t) * start[1] + t * end[1],
58 (1.0 - t) * start[2] + t * end[2],
59 (1.0 - t) * start[3] + t * end[3],
60 )
61}
62
63fn viridis_stops() -> [(f32, [f32; 4]); 6] {
64 [
65 (0.0, [68.0 / 255.0, 1.0 / 255.0, 84.0 / 255.0, 1.0]),
66 (0.2, [64.0 / 255.0, 67.0 / 255.0, 135.0 / 255.0, 1.0]),
67 (0.4, [41.0 / 255.0, 120.0 / 255.0, 142.0 / 255.0, 1.0]),
68 (0.6, [34.0 / 255.0, 167.0 / 255.0, 132.0 / 255.0, 1.0]),
69 (0.8, [121.0 / 255.0, 209.0 / 255.0, 81.0 / 255.0, 1.0]),
70 (1.0, [253.0 / 255.0, 231.0 / 255.0, 37.0 / 255.0, 1.0]),
71 ]
72}
73
74fn inferno_stops() -> [(f32, [f32; 4]); 6] {
75 [
76 (0.0, [0.0 / 255.0, 0.0 / 255.0, 4.0 / 255.0, 1.0]),
77 (0.2, [42.0 / 255.0, 11.0 / 255.0, 84.0 / 255.0, 1.0]),
78 (0.4, [101.0 / 255.0, 21.0 / 255.0, 110.0 / 255.0, 1.0]),
79 (0.6, [159.0 / 255.0, 42.0 / 255.0, 99.0 / 255.0, 1.0]),
80 (0.8, [225.0 / 255.0, 100.0 / 255.0, 40.0 / 255.0, 1.0]),
81 (1.0, [252.0 / 255.0, 1.0, 164.0 / 255.0, 1.0]),
82 ]
83}
84
85fn plasma_stops() -> [(f32, [f32; 4]); 6] {
86 [
87 (0.0, [13.0 / 255.0, 8.0 / 255.0, 135.0 / 255.0, 1.0]),
88 (0.2, [84.0 / 255.0, 3.0 / 255.0, 160.0 / 255.0, 1.0]),
89 (0.4, [139.0 / 255.0, 10.0 / 255.0, 165.0 / 255.0, 1.0]),
90 (0.6, [190.0 / 255.0, 56.0 / 255.0, 132.0 / 255.0, 1.0]),
91 (0.8, [240.0 / 255.0, 128.0 / 255.0, 77.0 / 255.0, 1.0]),
92 (1.0, [240.0 / 255.0, 249.0 / 255.0, 33.0 / 255.0, 1.0]),
93 ]
94}