Skip to content

Commit 5c8022f

Browse files
authored
Add spectral colormap (#11298)
1 parent e82109c commit 5c8022f

File tree

13 files changed

+156
-14
lines changed

13 files changed

+156
-14
lines changed

Cargo.lock

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3068,7 +3068,7 @@ checksum = "d6add3b8cff394282be81f3fc1a0605db594ed69890078ca6e2cab1c408bcf04"
30683068
[[package]]
30693069
name = "ecolor"
30703070
version = "0.32.3"
3071-
source = "git+https://github.com/emilk/egui.git?branch=main#c97c065a575ec6e657bb42872890a00d0fb391c1"
3071+
source = "git+https://github.com/emilk/egui.git?branch=main#48d903d8797d0869b5d2b43a22346dede7d471d2"
30723072
dependencies = [
30733073
"bytemuck",
30743074
"color-hex",
@@ -3085,7 +3085,7 @@ checksum = "18aade80d5e09429040243ce1143ddc08a92d7a22820ac512610410a4dd5214f"
30853085
[[package]]
30863086
name = "eframe"
30873087
version = "0.32.3"
3088-
source = "git+https://github.com/emilk/egui.git?branch=main#c97c065a575ec6e657bb42872890a00d0fb391c1"
3088+
source = "git+https://github.com/emilk/egui.git?branch=main#48d903d8797d0869b5d2b43a22346dede7d471d2"
30893089
dependencies = [
30903090
"ahash",
30913091
"bytemuck",
@@ -3123,7 +3123,7 @@ dependencies = [
31233123
[[package]]
31243124
name = "egui"
31253125
version = "0.32.3"
3126-
source = "git+https://github.com/emilk/egui.git?branch=main#c97c065a575ec6e657bb42872890a00d0fb391c1"
3126+
source = "git+https://github.com/emilk/egui.git?branch=main#48d903d8797d0869b5d2b43a22346dede7d471d2"
31273127
dependencies = [
31283128
"accesskit",
31293129
"ahash",
@@ -3143,7 +3143,7 @@ dependencies = [
31433143
[[package]]
31443144
name = "egui-wgpu"
31453145
version = "0.32.3"
3146-
source = "git+https://github.com/emilk/egui.git?branch=main#c97c065a575ec6e657bb42872890a00d0fb391c1"
3146+
source = "git+https://github.com/emilk/egui.git?branch=main#48d903d8797d0869b5d2b43a22346dede7d471d2"
31473147
dependencies = [
31483148
"ahash",
31493149
"bytemuck",
@@ -3162,7 +3162,7 @@ dependencies = [
31623162
[[package]]
31633163
name = "egui-winit"
31643164
version = "0.32.3"
3165-
source = "git+https://github.com/emilk/egui.git?branch=main#c97c065a575ec6e657bb42872890a00d0fb391c1"
3165+
source = "git+https://github.com/emilk/egui.git?branch=main#48d903d8797d0869b5d2b43a22346dede7d471d2"
31663166
dependencies = [
31673167
"accesskit_winit",
31683168
"arboard",
@@ -3225,7 +3225,7 @@ dependencies = [
32253225
[[package]]
32263226
name = "egui_extras"
32273227
version = "0.32.3"
3228-
source = "git+https://github.com/emilk/egui.git?branch=main#c97c065a575ec6e657bb42872890a00d0fb391c1"
3228+
source = "git+https://github.com/emilk/egui.git?branch=main#48d903d8797d0869b5d2b43a22346dede7d471d2"
32293229
dependencies = [
32303230
"ahash",
32313231
"egui",
@@ -3242,7 +3242,7 @@ dependencies = [
32423242
[[package]]
32433243
name = "egui_glow"
32443244
version = "0.32.3"
3245-
source = "git+https://github.com/emilk/egui.git?branch=main#c97c065a575ec6e657bb42872890a00d0fb391c1"
3245+
source = "git+https://github.com/emilk/egui.git?branch=main#48d903d8797d0869b5d2b43a22346dede7d471d2"
32463246
dependencies = [
32473247
"bytemuck",
32483248
"egui",
@@ -3258,7 +3258,7 @@ dependencies = [
32583258
[[package]]
32593259
name = "egui_kittest"
32603260
version = "0.32.3"
3261-
source = "git+https://github.com/emilk/egui.git?branch=main#c97c065a575ec6e657bb42872890a00d0fb391c1"
3261+
source = "git+https://github.com/emilk/egui.git?branch=main#48d903d8797d0869b5d2b43a22346dede7d471d2"
32623262
dependencies = [
32633263
"dify",
32643264
"eframe",
@@ -3332,7 +3332,7 @@ checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
33323332
[[package]]
33333333
name = "emath"
33343334
version = "0.32.3"
3335-
source = "git+https://github.com/emilk/egui.git?branch=main#c97c065a575ec6e657bb42872890a00d0fb391c1"
3335+
source = "git+https://github.com/emilk/egui.git?branch=main#48d903d8797d0869b5d2b43a22346dede7d471d2"
33363336
dependencies = [
33373337
"bytemuck",
33383338
"serde",
@@ -3449,7 +3449,7 @@ dependencies = [
34493449
[[package]]
34503450
name = "epaint"
34513451
version = "0.32.3"
3452-
source = "git+https://github.com/emilk/egui.git?branch=main#c97c065a575ec6e657bb42872890a00d0fb391c1"
3452+
source = "git+https://github.com/emilk/egui.git?branch=main#48d903d8797d0869b5d2b43a22346dede7d471d2"
34533453
dependencies = [
34543454
"ab_glyph",
34553455
"ahash",
@@ -3468,7 +3468,7 @@ dependencies = [
34683468
[[package]]
34693469
name = "epaint_default_fonts"
34703470
version = "0.32.3"
3471-
source = "git+https://github.com/emilk/egui.git?branch=main#c97c065a575ec6e657bb42872890a00d0fb391c1"
3471+
source = "git+https://github.com/emilk/egui.git?branch=main#48d903d8797d0869b5d2b43a22346dede7d471d2"
34723472

34733473
[[package]]
34743474
name = "equivalent"

crates/store/re_types/definitions/rerun/components/colormap.fbs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,4 +52,11 @@ enum Colormap: ubyte {
5252
/// It is especially suited for visualizing signed values.
5353
/// It interpolates from cyan to blue to dark gray to brass to yellow.
5454
CyanToYellow,
55+
56+
/// The Spectral colormap from Matplotlib.
57+
///
58+
/// This is a diverging colormap, often used to visualize data with a meaningful center point,
59+
/// where deviations from that center are important to highlight.
60+
/// It interpolates from red to orange to yellow to green to blue to violet.
61+
Spectral,
5562
}

crates/store/re_types/src/components/colormap.rs

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/store/re_types/src/components/colormap_ext.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ impl Colormap {
1414
| Self::Plasma
1515
| Self::Turbo
1616
| Self::Viridis
17-
| Self::CyanToYellow => {}
17+
| Self::CyanToYellow
18+
| Self::Spectral => {}
1819
}
1920

2021
match value {
@@ -25,6 +26,7 @@ impl Colormap {
2526
v if v == Self::Turbo as u8 => Some(Self::Turbo),
2627
v if v == Self::Viridis as u8 => Some(Self::Viridis),
2728
v if v == Self::CyanToYellow as u8 => Some(Self::CyanToYellow),
29+
v if v == Self::Spectral as u8 => Some(Self::Spectral),
2830
_ => None,
2931
}
3032
}

crates/viewer/re_renderer/shader/colormap.wgsl

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const COLORMAP_PLASMA: u32 = 4u;
99
const COLORMAP_TURBO: u32 = 5u;
1010
const COLORMAP_VIRIDIS: u32 = 6u;
1111
const COLORMAP_CYAN_TO_YELLOW: u32 = 7u;
12+
const COLORMAP_SPECTRAL: u32 = 8u;
1213

1314
/// Returns a gamma-space sRGB in 0-1 range.
1415
///
@@ -31,6 +32,8 @@ fn colormap_srgb(which: u32, t_unsaturated: f32) -> vec3f {
3132
return colormap_viridis_srgb(t);
3233
} else if which == COLORMAP_CYAN_TO_YELLOW {
3334
return colormap_cyan_to_yellow_srgb(t);
35+
} else if which == COLORMAP_SPECTRAL {
36+
return colormap_spectral_srgb(t);
3437
} else {
3538
return ERROR_RGBA.rgb;
3639
}
@@ -162,3 +165,17 @@ fn colormap_cyan_to_yellow_srgb(t: f32) -> vec3f {
162165
let u = t * 2. - 1.;
163166
return saturate(vec3f(1. + 3. * u, (1. + 3. * u * u) , 1. - 3. * u) / 4.);
164167
}
168+
169+
/// Returns a gamma-space sRGB in 0-1 range.
170+
/// The input (`t`) must be in the 0-1 range.
171+
/// This is a polynomial approximation from Spectral color map.
172+
fn colormap_spectral_srgb(t: f32) -> vec3f {
173+
let c0 = vec3f(0.584384543712538, 0.006424432561482, 0.231061410304836);
174+
let c1 = vec3f(3.768572852617221, 2.487082885717158, 2.821174312084977);
175+
let c2 = vec3f(-16.262574054760623, -6.243215992229093, -37.292187460541960);
176+
let c3 = vec3f(39.821464952234010, 39.932449794574126, 186.340471613899751);
177+
let c4 = vec3f(-46.140976850412727, -99.423798167148249, -388.532539629914481);
178+
let c5 = vec3f(12.716626092708825, 96.954180217298671, 360.627239851094203);
179+
let c6 = vec3f(5.942343111972585, -33.440386037285862, -123.635206049211334);
180+
return c0 + t * (c1 + t * (c2 + t * (c3 + t * (c4 + t * (c5 + t * c6)))));
181+
}

crates/viewer/re_renderer/src/colormap.rs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,19 @@ pub enum Colormap {
1919
Turbo = 5,
2020
Viridis = 6,
2121
CyanToYellow = 7,
22+
Spectral = 8,
2223
}
2324

2425
impl Colormap {
25-
pub const ALL: [Self; 7] = [
26+
pub const ALL: [Self; 8] = [
2627
Self::Grayscale,
2728
Self::Inferno,
2829
Self::Magma,
2930
Self::Plasma,
3031
Self::Turbo,
3132
Self::Viridis,
3233
Self::CyanToYellow,
34+
Self::Spectral,
3335
];
3436
}
3537

@@ -43,6 +45,7 @@ impl std::fmt::Display for Colormap {
4345
Self::Turbo => write!(f, "Turbo"),
4446
Self::Viridis => write!(f, "Viridis"),
4547
Self::CyanToYellow => write!(f, "CyanToYellow"),
48+
Self::Spectral => write!(f, "Spectral"),
4649
}
4750
}
4851
}
@@ -56,6 +59,7 @@ pub fn colormap_srgb(which: Colormap, t: f32) -> [u8; 4] {
5659
Colormap::Magma => colormap_magma_srgb(t),
5760
Colormap::Inferno => colormap_inferno_srgb(t),
5861
Colormap::CyanToYellow => colormap_cyan_to_yellow_srgb(t),
62+
Colormap::Spectral => colormap_spectral_srgb(t),
5963
}
6064
}
6165

@@ -211,3 +215,25 @@ pub fn colormap_cyan_to_yellow_srgb(t: f32) -> [u8; 4] {
211215
255,
212216
]
213217
}
218+
219+
/// Returns sRGB polynomial approximation from Spectral color map, assuming `t` is normalized.
220+
pub fn colormap_spectral_srgb(t: f32) -> [u8; 4] {
221+
const C0: Vec3A = Vec3A::new(0.584384543712538, 0.006424432561482, 0.231061410304836);
222+
const C1: Vec3A = Vec3A::new(3.768572852617221, 2.487082885717158, 2.821174312084977);
223+
const C2: Vec3A = Vec3A::new(-16.262574054760623, -6.243215992229093, -37.292187460541960);
224+
const C3: Vec3A = Vec3A::new(39.821464952234010, 39.932449794574126, 186.340471613899751);
225+
const C4: Vec3A = Vec3A::new(
226+
-46.140976850412727,
227+
-99.423798167148249,
228+
-388.532539629914481,
229+
);
230+
const C5: Vec3A = Vec3A::new(12.716626092708825, 96.954180217298671, 360.627239851094203);
231+
const C6: Vec3A = Vec3A::new(5.942343111972585, -33.440386037285862, -123.635206049211334);
232+
233+
debug_assert!((0.0..=1.0).contains(&t));
234+
235+
let c = C0 + t * (C1 + t * (C2 + t * (C3 + t * (C4 + t * (C5 + t * C6)))));
236+
237+
let c = c * 255.0;
238+
[c.x as u8, c.y as u8, c.z as u8, 255]
239+
}

crates/viewer/re_viewer/tests/app_kittest.rs

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,13 @@
22

33
use std::time::Duration;
44

5-
use egui_kittest::kittest::Queryable as _;
5+
use egui::accesskit::Role;
6+
use egui_kittest::{SnapshotOptions, kittest::Queryable as _};
67

8+
use re_test_context::TestContext;
9+
use re_types::components::Colormap;
710
use re_viewer::viewer_test_utils;
11+
use re_viewer_context::{MaybeMutRef, ViewerContext};
812

913
/// Navigates from welcome to settings screen and snapshots it.
1014
#[tokio::test]
@@ -36,3 +40,44 @@ async fn settings_screen() {
3640
);
3741
harness.snapshot("settings_screen");
3842
}
43+
44+
/// Tests the colormap selector UI with snapshot testing.
45+
/// This is defined here instead of in `re_viewer/tests` because it depends on `re_test_context`,
46+
/// which depends on `re_viewer_context`.
47+
#[test]
48+
fn colormap_selector_ui() {
49+
let mut test_context = TestContext::new();
50+
test_context.component_ui_registry = re_component_ui::create_component_ui_registry();
51+
re_data_ui::register_component_uis(&mut test_context.component_ui_registry);
52+
53+
let mut harness = test_context.setup_kittest_for_rendering().build_ui(|ui| {
54+
re_ui::apply_style_and_install_loaders(ui.ctx());
55+
56+
test_context.run(&ui.ctx().clone(), |ctx: &ViewerContext<'_>| {
57+
ui.horizontal(|ui| {
58+
ui.label("Colormap:");
59+
60+
let mut test_colormap = Colormap::Spectral;
61+
let mut colormap_ref = MaybeMutRef::MutRef(&mut test_colormap);
62+
63+
re_viewer_context::gpu_bridge::colormap_edit_or_view_ui(ctx, ui, &mut colormap_ref);
64+
});
65+
});
66+
});
67+
68+
harness.run();
69+
harness.fit_contents();
70+
harness.snapshot("colormap_selector_closed");
71+
72+
// give the combo box some room to open
73+
harness.set_size(egui::Vec2::new(200.0, 250.0));
74+
harness.get_by_role(Role::ComboBox).click(); // open combo box
75+
harness.run();
76+
77+
harness.fit_contents();
78+
harness.run();
79+
harness.snapshot_options(
80+
"colormap_selector_open",
81+
&SnapshotOptions::new().threshold(2.0),
82+
);
83+
}
Lines changed: 3 additions & 0 deletions
Loading
Lines changed: 3 additions & 0 deletions
Loading

crates/viewer/re_viewer_context/src/gpu_bridge/colormap.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,5 +157,6 @@ pub fn colormap_to_re_renderer(colormap: re_types::components::Colormap) -> re_r
157157
re_types::components::Colormap::Turbo => re_renderer::Colormap::Turbo,
158158
re_types::components::Colormap::Viridis => re_renderer::Colormap::Viridis,
159159
re_types::components::Colormap::CyanToYellow => re_renderer::Colormap::CyanToYellow,
160+
re_types::components::Colormap::Spectral => re_renderer::Colormap::Spectral,
160161
}
161162
}

0 commit comments

Comments
 (0)