Skip to content

Commit 2104176

Browse files
committed
Merge branch 'main' into antoine/table-filter
# Conflicts: # crates/viewer/re_ui/data/dark_theme.ron # crates/viewer/re_ui/data/light_theme.ron # crates/viewer/re_ui/src/design_tokens.rs # crates/viewer/re_ui/src/syntax_highlighting.rs
2 parents 9790e18 + 55b12f2 commit 2104176

File tree

125 files changed

+2051
-374
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

125 files changed

+2051
-374
lines changed

ARCHITECTURE.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -98,11 +98,11 @@ Of course, this will only take us so far. In the future we plan on caching queri
9898
Here is an overview of the crates included in the project:
9999

100100
<picture>
101-
<img src="https://static.rerun.io/crates/1147f3775a6432e22c5015276d0938e9411d54a3/full.png" alt="">
102-
<source media="(max-width: 480px)" srcset="https://static.rerun.io/crates/1147f3775a6432e22c5015276d0938e9411d54a3/480w.png">
103-
<source media="(max-width: 768px)" srcset="https://static.rerun.io/crates/1147f3775a6432e22c5015276d0938e9411d54a3/768w.png">
104-
<source media="(max-width: 1024px)" srcset="https://static.rerun.io/crates/1147f3775a6432e22c5015276d0938e9411d54a3/1024w.png">
105-
<source media="(max-width: 1200px)" srcset="https://static.rerun.io/crates/1147f3775a6432e22c5015276d0938e9411d54a3/1200w.png">
101+
<img src="https://static.rerun.io/crates/bfe6853639d2427dd424e529b5d70950e2f61840/full.png" alt="">
102+
<source media="(max-width: 480px)" srcset="https://static.rerun.io/crates/bfe6853639d2427dd424e529b5d70950e2f61840/480w.png">
103+
<source media="(max-width: 768px)" srcset="https://static.rerun.io/crates/bfe6853639d2427dd424e529b5d70950e2f61840/768w.png">
104+
<source media="(max-width: 1024px)" srcset="https://static.rerun.io/crates/bfe6853639d2427dd424e529b5d70950e2f61840/1024w.png">
105+
<source media="(max-width: 1200px)" srcset="https://static.rerun.io/crates/bfe6853639d2427dd424e529b5d70950e2f61840/1200w.png">
106106
</picture>
107107

108108
<!-- !!! IMPORTANT!!!
@@ -134,6 +134,7 @@ Update instructions:
134134

135135
| Crate | Description |
136136
|-----------------------|------------------------------------------------------------------------------------------------------------|
137+
| re_arrow_ui | Show arrow data in a tree of rerun list_items and format arrow with syntax highlighting. |
137138
| re_blueprint_tree | The UI for the blueprint tree in the left panel. |
138139
| re_redap_browser | The UI and communication to implement the in-viewer redap server browser. |
139140
| re_chunk_store_ui | A chunk store browser UI. |

Cargo.lock

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7212,6 +7212,20 @@ dependencies = [
72127212
"web-sys",
72137213
]
72147214

7215+
[[package]]
7216+
name = "re_arrow_ui"
7217+
version = "0.25.0-alpha.1+dev"
7218+
dependencies = [
7219+
"arrow",
7220+
"egui",
7221+
"egui_kittest",
7222+
"re_arrow_util",
7223+
"re_format",
7224+
"re_tracing",
7225+
"re_types",
7226+
"re_ui",
7227+
]
7228+
72157229
[[package]]
72167230
name = "re_arrow_util"
72177231
version = "0.25.0-alpha.1+dev"
@@ -7390,6 +7404,7 @@ dependencies = [
73907404
"egui",
73917405
"egui_extras",
73927406
"itertools 0.14.0",
7407+
"re_arrow_ui",
73937408
"re_byte_size",
73947409
"re_chunk_store",
73957410
"re_format",
@@ -7529,6 +7544,7 @@ dependencies = [
75297544
"egui_plot",
75307545
"itertools 0.14.0",
75317546
"jiff",
7547+
"re_arrow_ui",
75327548
"re_arrow_util",
75337549
"re_byte_size",
75347550
"re_capabilities",
@@ -7712,6 +7728,7 @@ dependencies = [
77127728
name = "re_format"
77137729
version = "0.25.0-alpha.1+dev"
77147730
dependencies = [
7731+
"half",
77157732
"num-traits",
77167733
]
77177734

@@ -8607,7 +8624,6 @@ version = "0.25.0-alpha.1+dev"
86078624
dependencies = [
86088625
"ahash",
86098626
"anyhow",
8610-
"arrow",
86118627
"eframe",
86128628
"egui",
86138629
"egui_commonmark",
@@ -8622,14 +8638,12 @@ dependencies = [
86228638
"parking_lot",
86238639
"rand 0.8.5",
86248640
"re_analytics",
8625-
"re_arrow_util",
86268641
"re_build_tools",
86278642
"re_entity_db",
86288643
"re_format",
86298644
"re_log",
86308645
"re_log_types",
86318646
"re_tracing",
8632-
"re_types",
86338647
"ron",
86348648
"serde",
86358649
"smallvec",
@@ -9067,6 +9081,7 @@ dependencies = [
90679081
"ndarray",
90689082
"nohash-hasher",
90699083
"parking_lot",
9084+
"re_arrow_ui",
90709085
"re_arrow_util",
90719086
"re_byte_size",
90729087
"re_chunk",

Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ re_uri = { path = "crates/utils/re_uri", version = "=0.25.0-alpha.1", default-fe
113113
re_video = { path = "crates/utils/re_video", version = "=0.25.0-alpha.1", default-features = false }
114114

115115
# crates/viewer:
116+
re_arrow_ui = { path = "crates/viewer/re_arrow_ui", version = "=0.25.0-alpha.1", default-features = false }
116117
re_blueprint_tree = { path = "crates/viewer/re_blueprint_tree", version = "=0.25.0-alpha.1", default-features = false }
117118
re_redap_browser = { path = "crates/viewer/re_redap_browser", version = "=0.25.0-alpha.1", default-features = false }
118119
re_component_ui = { path = "crates/viewer/re_component_ui", version = "=0.25.0-alpha.1", default-features = false }
@@ -430,6 +431,7 @@ debug = false
430431

431432
[profile.dev.package]
432433
"re_analytics".debug = true
434+
"re_arrow_ui".debug = true
433435
"re_arrow_util".debug = true
434436
"re_auth".debug = true
435437
"re_blueprint_tree".debug = true

crates/store/re_types/src/tensor_data.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -365,7 +365,7 @@ impl TensorElement {
365365
Self::I16(val) => re_format::format_int(*val),
366366
Self::I32(val) => re_format::format_int(*val),
367367
Self::I64(val) => re_format::format_int(*val),
368-
Self::F16(val) => re_format::format_f32(val.to_f32()),
368+
Self::F16(val) => re_format::format_f16(*val),
369369
Self::F32(val) => re_format::format_f32(*val),
370370
Self::F64(val) => re_format::format_f64(*val),
371371
}

crates/utils/re_format/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,5 @@ all-features = true
2121

2222
[dependencies]
2323
num-traits.workspace = true
24+
half.workspace = true
2425
# Do NOT add any complex dependencies here. Keep this crate lightweight!

crates/utils/re_format/src/lib.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,16 @@ pub struct FloatFormatOptions {
161161
}
162162

163163
impl FloatFormatOptions {
164+
/// Default options for formatting an [`half::f16`].
165+
#[allow(non_upper_case_globals)]
166+
pub const DEFAULT_f16: Self = Self {
167+
always_sign: false,
168+
precision: 5,
169+
num_decimals: None,
170+
strip_trailing_zeros: true,
171+
min_decimals_for_thousands_separators: 6,
172+
};
173+
164174
/// Default options for formatting an [`f32`].
165175
#[allow(non_upper_case_globals)]
166176
pub const DEFAULT_f32: Self = Self {
@@ -316,6 +326,14 @@ pub fn format_f32(value: f32) -> String {
316326
FloatFormatOptions::DEFAULT_f32.format(value)
317327
}
318328

329+
/// Format a number with about 5 decimals of precision.
330+
///
331+
/// The returned value is for human eyes only, and can not be parsed
332+
/// by the normal `f64::from_str` function.
333+
pub fn format_f16(value: half::f16) -> String {
334+
FloatFormatOptions::DEFAULT_f16.format(value)
335+
}
336+
319337
/// Format a latitude or longitude value.
320338
///
321339
/// For human eyes only.
@@ -395,6 +413,30 @@ fn test_format_f64() {
395413
}
396414
}
397415

416+
#[test]
417+
fn test_format_f16() {
418+
use half::f16;
419+
420+
let cases = [
421+
(f16::from_f32(f32::NAN), "NaN"),
422+
(f16::INFINITY, "∞"),
423+
(f16::NEG_INFINITY, "−∞"),
424+
(f16::ZERO, "0"),
425+
(f16::from_f32(42.0), "42"),
426+
(f16::from_f32(-42.0), "−42"),
427+
(f16::from_f32(-4.20), "−4.1992"), // f16 precision limitation
428+
(f16::from_f32(12_345.0), "12 344"), // f16 precision limitation
429+
(f16::PI, "3.1406"), // f16 precision limitation
430+
];
431+
for (value, expected) in cases {
432+
let got = format_f16(value);
433+
assert_eq!(
434+
got, expected,
435+
"Expected to format {value} as '{expected}', but got '{got}'"
436+
);
437+
}
438+
}
439+
398440
#[test]
399441
fn test_format_f64_custom() {
400442
let cases = [(
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
[package]
2+
authors.workspace = true
3+
description = "Show arrow data in a tree of rerun list_items and format arrow with syntax highlighting."
4+
edition.workspace = true
5+
homepage.workspace = true
6+
license.workspace = true
7+
name = "re_arrow_ui"
8+
publish = true
9+
readme = "README.md"
10+
repository.workspace = true
11+
rust-version.workspace = true
12+
version.workspace = true
13+
include.workspace = true
14+
15+
[lints]
16+
workspace = true
17+
18+
[dependencies]
19+
re_arrow_util.workspace = true
20+
re_format.workspace = true
21+
re_tracing.workspace = true
22+
re_ui.workspace = true
23+
24+
arrow.workspace = true
25+
egui.workspace = true
26+
27+
[dev-dependencies]
28+
egui_kittest.workspace = true
29+
re_types.workspace = true
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# re_arrow_ui
2+
3+
Part of the [`rerun`](https://github.com/rerun-io/rerun) family of crates.
4+
5+
[![Latest version](https://img.shields.io/crates/v/re_arrow_ui.svg?speculative-link)](https://crates.io/crates/re_arrow_ui?speculative-link)
6+
[![Documentation](https://docs.rs/re_arrow_ui/badge.svg?speculative-link)](https://docs.rs/re_arrow_ui?speculative-link)
7+
![MIT](https://img.shields.io/badge/license-MIT-blue.svg)
8+
![Apache](https://img.shields.io/badge/license-Apache-blue.svg)
9+
10+
Show arrow data in a tree of rerun list_items and format arrow with syntax highlighting.
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
use egui::{Id, RichText, Stroke, StrokeKind, Tooltip, Ui, WidgetText};
2+
3+
use re_format::format_uint;
4+
use re_ui::UiExt as _;
5+
use re_ui::list_item::{LabelContent, PropertyContent, list_item_scope};
6+
use re_ui::syntax_highlighting::SyntaxHighlightedBuilder;
7+
8+
use crate::datatype_ui::DataTypeUi;
9+
use crate::show_index::ShowIndex;
10+
11+
enum NodeLabel {
12+
/// The index to *display*. May be different from the actual index of the value.
13+
///
14+
/// E.g. in a [`arrow::array::ListArray`], this is the index in the child list. The index passed to
15+
/// [`ArrowNode::show`] is the index in the parent array.
16+
///
17+
/// Also see [`crate::show_index::list_ui`] for a more thorough explanation.
18+
Index(usize),
19+
Name(String),
20+
Custom(WidgetText),
21+
}
22+
23+
/// Display an item of an Arrow array in a list item with some label.
24+
pub struct ArrowNode<'a> {
25+
label: NodeLabel,
26+
values: &'a dyn ShowIndex,
27+
}
28+
29+
impl<'a> ArrowNode<'a> {
30+
/// Create a new [`ArrowNode`] with a custom label
31+
pub fn custom(name: impl Into<WidgetText>, values: &'a dyn ShowIndex) -> Self {
32+
Self {
33+
label: NodeLabel::Custom(name.into()),
34+
values,
35+
}
36+
}
37+
38+
/// Create a new [`ArrowNode`] from an Arrow field.
39+
///
40+
/// This will set the name to the fields name.
41+
pub fn field(field: &'a arrow::datatypes::Field, values: &'a dyn ShowIndex) -> Self {
42+
Self {
43+
label: NodeLabel::Name(field.name().to_owned()),
44+
values,
45+
}
46+
}
47+
48+
/// The index to *display* (See [`NodeLabel::Index`]).
49+
pub fn index(idx: usize, values: &'a dyn ShowIndex) -> Self {
50+
Self {
51+
label: NodeLabel::Index(idx),
52+
values,
53+
}
54+
}
55+
56+
/// Index is the index of the *value* to display.
57+
///
58+
/// Can be different from [`ArrowNode::index`] (the index to display) e.g. in a sliced array.
59+
/// See also [`NodeLabel::Index`].
60+
pub fn show(self, ui: &mut Ui, index: usize) {
61+
let label = match self.label {
62+
NodeLabel::Index(idx) => {
63+
let mut builder = SyntaxHighlightedBuilder::new(ui.style());
64+
builder.code_index(&format_uint(idx));
65+
builder.into_widget_text()
66+
}
67+
NodeLabel::Name(name) => {
68+
let mut builder = SyntaxHighlightedBuilder::new(ui.style());
69+
builder.code_identifier(&name);
70+
builder.into_widget_text()
71+
}
72+
NodeLabel::Custom(name) => name,
73+
};
74+
75+
let mut value = SyntaxHighlightedBuilder::new(ui.style());
76+
let result = self.values.write(index, &mut value);
77+
let value = match result {
78+
Ok(()) => value.into_widget_text(),
79+
Err(err) => RichText::new(format!("Error: {err}"))
80+
.color(ui.tokens().error_fg_color)
81+
.into(),
82+
};
83+
84+
let nested = self.values.is_item_nested();
85+
let data_type = self.values.array().data_type();
86+
let data_type_ui = DataTypeUi::new(data_type);
87+
88+
let item = ui.list_item();
89+
// We *don't* use index for the ID, since it might change across timesteps,
90+
// while referring the same logical data.
91+
let id = ui.id().with(label.text());
92+
let content = PropertyContent::new(label)
93+
.value_fn(|ui, visuals| {
94+
ui.horizontal(|ui| {
95+
egui::Sides::new().shrink_left().show(
96+
ui,
97+
|ui| {
98+
if visuals.is_collapsible() && visuals.openness() != 0.0 {
99+
if visuals.openness() == 1.0 {
100+
return;
101+
}
102+
ui.set_opacity(1.0 - visuals.openness());
103+
}
104+
ui.label(value);
105+
},
106+
|ui| {
107+
let tooltip_open =
108+
Tooltip::was_tooltip_open_last_frame(ui.ctx(), ui.next_auto_id());
109+
// Keep showing the data type when the tooltip is open, so the
110+
// user can interact with it.
111+
if visuals.hovered || tooltip_open {
112+
// TODO(lucas): We should show the nullability here too
113+
let response =
114+
ui.small(RichText::new(&data_type_ui.type_name).strong());
115+
ui.painter().rect_stroke(
116+
response.rect.expand(2.0),
117+
4.0,
118+
Stroke::new(1.0, visuals.text_color()),
119+
StrokeKind::Middle,
120+
);
121+
122+
if let Some(content) = data_type_ui.content {
123+
response.on_hover_ui(|ui| {
124+
list_item_scope(
125+
ui,
126+
Id::new("arrow data type hover"),
127+
|ui| {
128+
ui.list_item().show_hierarchical_with_children(
129+
ui,
130+
Id::new("arrow data type hover item"),
131+
true,
132+
LabelContent::new(data_type_ui.type_name),
133+
content,
134+
);
135+
},
136+
);
137+
});
138+
}
139+
}
140+
},
141+
);
142+
});
143+
})
144+
.show_only_when_collapsed(false);
145+
146+
if nested {
147+
item.show_hierarchical_with_children(ui, id, false, content, |ui| {
148+
list_item_scope(ui, id.with("child_scope"), |ui| {
149+
self.values.show(index, ui);
150+
});
151+
});
152+
} else {
153+
item.show_hierarchical(ui, content);
154+
}
155+
}
156+
}

0 commit comments

Comments
 (0)