ak_vis/color_palette/
scalar.rs

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}