Skip to content

Commit

Permalink
Update to latest egui master (#6683)
Browse files Browse the repository at this point in the history
### What
With this update, we now format numbers in `DragValues` using
`re_format`, i.e. with thousands separators. This makes large editable
components easier to read, and makes our UI more consistent.


![image](https://github.com/rerun-io/rerun/assets/1148717/c7f3367e-45d7-4967-9bfe-6ac1cd98c0ad)

* Also closes #6681
* Also includes a better fix for
#6639

### Checklist
* [x] I have read and agree to [Contributor
Guide](https://github.com/rerun-io/rerun/blob/main/CONTRIBUTING.md) and
the [Code of
Conduct](https://github.com/rerun-io/rerun/blob/main/CODE_OF_CONDUCT.md)
* [x] I've included a screenshot or gif (if applicable)
* [x] I have tested the web demo (if applicable):
* Using examples from latest `main` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/6683?manifest_url=https://app.rerun.io/version/main/examples_manifest.json)
* Using full set of examples from `nightly` build:
[rerun.io/viewer](https://rerun.io/viewer/pr/6683?manifest_url=https://app.rerun.io/version/nightly/examples_manifest.json)
* [x] The PR title and labels are set such as to maximize their
usefulness for the next release's CHANGELOG
* [x] If applicable, add a new check to the [release
checklist](https://github.com/rerun-io/rerun/blob/main/tests/python/release_checklist)!

- [PR Build Summary](https://build.rerun.io/pr/6683)
- [Recent benchmark results](https://build.rerun.io/graphs/crates.html)
- [Wasm size tracking](https://build.rerun.io/graphs/sizes.html)

To run all checks from `main`, comment on the PR with `@rerun-bot
full-check`.
  • Loading branch information
emilk authored Jul 1, 2024
1 parent a9b8142 commit edb6ede
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 96 deletions.
21 changes: 11 additions & 10 deletions Cargo.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1553,7 +1553,7 @@ checksum = "68b0cf012f1230e43cd00ebb729c6bb58707ecfa8ad08b52ef3a4ccd2697fc30"
[[package]]
name = "ecolor"
version = "0.27.2"
source = "git+https://github.com/emilk/egui.git?rev=d4e8966aac9347965f8d02310ecf2c9f23bb9bbc#d4e8966aac9347965f8d02310ecf2c9f23bb9bbc"
source = "git+https://github.com/emilk/egui.git?rev=d2717180656d640e606a46932718dbef59fd5ef7#d2717180656d640e606a46932718dbef59fd5ef7"
dependencies = [
"bytemuck",
"emath",
Expand All @@ -1563,7 +1563,7 @@ dependencies = [
[[package]]
name = "eframe"
version = "0.27.2"
source = "git+https://github.com/emilk/egui.git?rev=d4e8966aac9347965f8d02310ecf2c9f23bb9bbc#d4e8966aac9347965f8d02310ecf2c9f23bb9bbc"
source = "git+https://github.com/emilk/egui.git?rev=d2717180656d640e606a46932718dbef59fd5ef7#d2717180656d640e606a46932718dbef59fd5ef7"
dependencies = [
"ahash",
"bytemuck",
Expand Down Expand Up @@ -1599,7 +1599,7 @@ dependencies = [
[[package]]
name = "egui"
version = "0.27.2"
source = "git+https://github.com/emilk/egui.git?rev=d4e8966aac9347965f8d02310ecf2c9f23bb9bbc#d4e8966aac9347965f8d02310ecf2c9f23bb9bbc"
source = "git+https://github.com/emilk/egui.git?rev=d2717180656d640e606a46932718dbef59fd5ef7#d2717180656d640e606a46932718dbef59fd5ef7"
dependencies = [
"accesskit",
"ahash",
Expand All @@ -1616,7 +1616,7 @@ dependencies = [
[[package]]
name = "egui-wgpu"
version = "0.27.2"
source = "git+https://github.com/emilk/egui.git?rev=d4e8966aac9347965f8d02310ecf2c9f23bb9bbc#d4e8966aac9347965f8d02310ecf2c9f23bb9bbc"
source = "git+https://github.com/emilk/egui.git?rev=d2717180656d640e606a46932718dbef59fd5ef7#d2717180656d640e606a46932718dbef59fd5ef7"
dependencies = [
"ahash",
"bytemuck",
Expand All @@ -1635,7 +1635,7 @@ dependencies = [
[[package]]
name = "egui-winit"
version = "0.27.2"
source = "git+https://github.com/emilk/egui.git?rev=d4e8966aac9347965f8d02310ecf2c9f23bb9bbc#d4e8966aac9347965f8d02310ecf2c9f23bb9bbc"
source = "git+https://github.com/emilk/egui.git?rev=d2717180656d640e606a46932718dbef59fd5ef7#d2717180656d640e606a46932718dbef59fd5ef7"
dependencies = [
"accesskit_winit",
"ahash",
Expand Down Expand Up @@ -1675,7 +1675,7 @@ dependencies = [
[[package]]
name = "egui_extras"
version = "0.27.2"
source = "git+https://github.com/emilk/egui.git?rev=d4e8966aac9347965f8d02310ecf2c9f23bb9bbc#d4e8966aac9347965f8d02310ecf2c9f23bb9bbc"
source = "git+https://github.com/emilk/egui.git?rev=d2717180656d640e606a46932718dbef59fd5ef7#d2717180656d640e606a46932718dbef59fd5ef7"
dependencies = [
"ahash",
"egui",
Expand All @@ -1691,7 +1691,7 @@ dependencies = [
[[package]]
name = "egui_glow"
version = "0.27.2"
source = "git+https://github.com/emilk/egui.git?rev=d4e8966aac9347965f8d02310ecf2c9f23bb9bbc#d4e8966aac9347965f8d02310ecf2c9f23bb9bbc"
source = "git+https://github.com/emilk/egui.git?rev=d2717180656d640e606a46932718dbef59fd5ef7#d2717180656d640e606a46932718dbef59fd5ef7"
dependencies = [
"ahash",
"bytemuck",
Expand All @@ -1709,10 +1709,11 @@ dependencies = [
[[package]]
name = "egui_plot"
version = "0.27.2"
source = "git+https://github.com/emilk/egui.git?rev=d4e8966aac9347965f8d02310ecf2c9f23bb9bbc#d4e8966aac9347965f8d02310ecf2c9f23bb9bbc"
source = "git+https://github.com/emilk/egui.git?rev=d2717180656d640e606a46932718dbef59fd5ef7#d2717180656d640e606a46932718dbef59fd5ef7"
dependencies = [
"ahash",
"egui",
"emath",
]

[[package]]
Expand Down Expand Up @@ -1752,7 +1753,7 @@ checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07"
[[package]]
name = "emath"
version = "0.27.2"
source = "git+https://github.com/emilk/egui.git?rev=d4e8966aac9347965f8d02310ecf2c9f23bb9bbc#d4e8966aac9347965f8d02310ecf2c9f23bb9bbc"
source = "git+https://github.com/emilk/egui.git?rev=d2717180656d640e606a46932718dbef59fd5ef7#d2717180656d640e606a46932718dbef59fd5ef7"
dependencies = [
"bytemuck",
"serde",
Expand Down Expand Up @@ -1853,7 +1854,7 @@ dependencies = [
[[package]]
name = "epaint"
version = "0.27.2"
source = "git+https://github.com/emilk/egui.git?rev=d4e8966aac9347965f8d02310ecf2c9f23bb9bbc#d4e8966aac9347965f8d02310ecf2c9f23bb9bbc"
source = "git+https://github.com/emilk/egui.git?rev=d2717180656d640e606a46932718dbef59fd5ef7#d2717180656d640e606a46932718dbef59fd5ef7"
dependencies = [
"ab_glyph",
"ahash",
Expand Down
14 changes: 7 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -481,13 +481,13 @@ missing_errors_doc = "allow"
# As a last resport, patch with a commit to our own repository.
# ALWAYS document what PR the commit hash is part of, or when it was merged into the upstream trunk.

ecolor = { git = "https://github.com/emilk/egui.git", rev = "d4e8966aac9347965f8d02310ecf2c9f23bb9bbc" } # egui master 2024-06-28
eframe = { git = "https://github.com/emilk/egui.git", rev = "d4e8966aac9347965f8d02310ecf2c9f23bb9bbc" } # egui master 2024-06-28
egui = { git = "https://github.com/emilk/egui.git", rev = "d4e8966aac9347965f8d02310ecf2c9f23bb9bbc" } # egui master 2024-06-28
egui_extras = { git = "https://github.com/emilk/egui.git", rev = "d4e8966aac9347965f8d02310ecf2c9f23bb9bbc" } # egui master 2024-06-28
egui_plot = { git = "https://github.com/emilk/egui.git", rev = "d4e8966aac9347965f8d02310ecf2c9f23bb9bbc" } # egui master 2024-06-28
egui-wgpu = { git = "https://github.com/emilk/egui.git", rev = "d4e8966aac9347965f8d02310ecf2c9f23bb9bbc" } # egui master 2024-06-28
emath = { git = "https://github.com/emilk/egui.git", rev = "d4e8966aac9347965f8d02310ecf2c9f23bb9bbc" } # egui master 2024-06-28
ecolor = { git = "https://github.com/emilk/egui.git", rev = "d2717180656d640e606a46932718dbef59fd5ef7" } # egui master 2024-07-01
eframe = { git = "https://github.com/emilk/egui.git", rev = "d2717180656d640e606a46932718dbef59fd5ef7" } # egui master 2024-07-01
egui = { git = "https://github.com/emilk/egui.git", rev = "d2717180656d640e606a46932718dbef59fd5ef7" } # egui master 2024-07-01
egui_extras = { git = "https://github.com/emilk/egui.git", rev = "d2717180656d640e606a46932718dbef59fd5ef7" } # egui master 2024-07-01
egui_plot = { git = "https://github.com/emilk/egui.git", rev = "d2717180656d640e606a46932718dbef59fd5ef7" } # egui master 2024-07-01
egui-wgpu = { git = "https://github.com/emilk/egui.git", rev = "d2717180656d640e606a46932718dbef59fd5ef7" } # egui master 2024-07-01
emath = { git = "https://github.com/emilk/egui.git", rev = "d2717180656d640e606a46932718dbef59fd5ef7" } # egui master 2024-07-01

# Useful while developing:
# ecolor = { path = "../../egui/crates/ecolor" }
Expand Down
14 changes: 14 additions & 0 deletions crates/re_format/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,20 @@ fn test_format_f64_custom() {
}
}

/// Parses a number, ignoring whitespace (e.g. thousand separators),
/// and treating the special minus character `MINUS` (−) as a minus sign.
pub fn parse_f64(text: &str) -> Option<f64> {
let text: String = text
.chars()
// Ignore whitespace (trailing, leading, and thousands separators):
.filter(|c| !c.is_whitespace())
// Replace special minus character with normal minus (hyphen):
.map(|c| if c == '−' { '-' } else { c })
.collect();

text.parse().ok()
}

/// Pretty format a large number by using SI notation (base 10), e.g.
///
/// ```
Expand Down
54 changes: 27 additions & 27 deletions crates/re_space_view_time_series/src/space_view_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ use re_viewport_blueprint::ViewProperty;

use crate::line_visualizer_system::SeriesLineSystem;
use crate::point_visualizer_system::SeriesPointSystem;
use crate::util::next_up_f64;
use crate::PlotSeriesKind;

// ---
Expand Down Expand Up @@ -277,13 +276,8 @@ impl SpaceViewClass for TimeSeriesSpaceView {

let scalar_axis =
ViewProperty::from_archetype::<ScalarAxis>(blueprint_db, ctx.blueprint_query, view_id);
let mut y_range = scalar_axis.component_or_fallback::<Range1D>(ctx, self, state)?;
if y_range.start() >= y_range.end() {
// Ensure that the range is valid - egui_plot might debug_assert otherwise.
// `next_up_f64` should be sufficient, but empirically it's not always enough
// (likely we're loosing precision due to some calculations down the line)
*y_range.end_mut() = y_range.start() + 1.0;
}
let y_range = scalar_axis.component_or_fallback::<Range1D>(ctx, self, state)?;
let y_range = make_range_sane(y_range);

let y_zoom_lock =
scalar_axis.component_or_fallback::<LockRangeDuringZoom>(ctx, self, state)?;
Expand Down Expand Up @@ -353,15 +347,14 @@ impl SpaceViewClass for TimeSeriesSpaceView {
.id(crate::plot_id(query.space_view_id))
.auto_bounds([true, false].into()) // Never use y auto bounds: we dictated bounds via blueprint under all circumstances.
.allow_zoom([true, !lock_y_during_zoom])
.x_axis_formatter(move |time, _, _| {
.x_axis_formatter(move |time, _| {
format_time(
time_type,
(time.value as i64).saturating_add(time_offset),
time_zone_for_timestamps,
)
})
.y_axis_formatter(move |mark, _, _| format_y_axis(mark))
.y_axis_width(3) // in digits
.y_axis_formatter(move |mark, _| format_y_axis(mark))
.label_formatter(move |name, value| {
let name = if name.is_empty() { "y" } else { name };
let label = time_type.format(
Expand Down Expand Up @@ -646,24 +639,31 @@ impl TypedComponentFallbackProvider<Range1D> for TimeSeriesSpaceView {
ctx.view_state
.as_any()
.downcast_ref::<TimeSeriesSpaceViewState>()
.map(|s| {
let mut range = s.scalar_range;
.map(|s| make_range_sane(s.scalar_range))
.unwrap_or_default()
}
}

// egui_plot can't handle a zero or negative range.
// Enforce a minimum range.
if !range.start().is_normal() {
*range.start_mut() = -1.0;
}
if !range.end().is_normal() {
*range.end_mut() = 1.0;
}
if range.start() >= range.end() {
*range.start_mut() = next_up_f64(range.end());
}
/// Make sure the range is finite and positive, or `egui_plot` might be buggy.
fn make_range_sane(y_range: Range1D) -> Range1D {
let (mut start, mut end) = (y_range.start(), y_range.end());

range
})
.unwrap_or_default()
if !start.is_normal() {
start = -1.0;
}
if !end.is_normal() {
end = 1.0;
}

if end < start {
(start, end) = (end, start);
}

if end <= start {
let center = (start + end) / 2.0;
Range1D::new(center - 1.0, center + 1.0)
} else {
Range1D::new(start, end)
}
}

Expand Down
50 changes: 0 additions & 50 deletions crates/re_space_view_time_series/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -269,53 +269,3 @@ fn add_series_runs(
all_series.push(series);
}
}

/// Returns the least number greater than `self`.
///
/// Unstable feature in Rust. This is a copy of the implementation from the standard library.
///
/// Let `TINY` be the smallest representable positive `f64`. Then,
/// - if `self.is_nan()`, this returns `self`;
/// - if `self` is [`NEG_INFINITY`], this returns [`MIN`];
/// - if `self` is `-TINY`, this returns -0.0;
/// - if `self` is -0.0 or +0.0, this returns `TINY`;
/// - if `self` is [`MAX`] or [`INFINITY`], this returns [`INFINITY`];
/// - otherwise the unique least value greater than `self` is returned.
///
/// The identity `x.next_up() == -(-x).next_down()` holds for all non-NaN `x`. When `x`
/// is finite `x == x.next_up().next_down()` also holds.
///
/// ```ignore
/// #![feature(float_next_up_down)]
/// // f64::EPSILON is the difference between 1.0 and the next number up.
/// assert_eq!(1.0f64.next_up(), 1.0 + f64::EPSILON);
/// // But not for most numbers.
/// assert!(0.1f64.next_up() < 0.1 + f64::EPSILON);
/// assert_eq!(9007199254740992f64.next_up(), 9007199254740994.0);
/// ```
///
/// [`NEG_INFINITY`]: f64::NEG_INFINITY
/// [`INFINITY`]: f64::INFINITY
/// [`MIN`]: f64::MIN
/// [`MAX`]: f64::MAX
pub fn next_up_f64(this: f64) -> f64 {
// We must use strictly integer arithmetic to prevent denormals from
// flushing to zero after an arithmetic operation on some platforms.
const TINY_BITS: u64 = 0x1; // Smallest positive f64.
const CLEAR_SIGN_MASK: u64 = 0x7fff_ffff_ffff_ffff;

let bits = this.to_bits();
if this.is_nan() || bits == f64::INFINITY.to_bits() {
return this;
}

let abs = bits & CLEAR_SIGN_MASK;
let next_bits = if abs == 0 {
TINY_BITS
} else if bits == abs {
bits + 1
} else {
bits - 1
};
f64::from_bits(next_bits)
}
47 changes: 47 additions & 0 deletions crates/re_ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ mod section_collapsing_header;
pub mod toasts;
mod ui_ext;

use egui::NumExt as _;

pub use self::{
command::{UICommand, UICommandSender},
command_palette::CommandPalette,
Expand Down Expand Up @@ -89,6 +91,51 @@ pub fn apply_style_and_install_loaders(egui_ctx: &egui::Context) {
);

design_tokens().apply(egui_ctx);

egui_ctx.style_mut(|style| {
style.number_formatter = egui::style::NumberFormatter::new(format_with_decimals_in_range);
});
}

fn format_with_decimals_in_range(
value: f64,
decimal_range: std::ops::RangeInclusive<usize>,
) -> String {
fn format_with_decimals(value: f64, decimals: usize) -> String {
re_format::FloatFormatOptions::DEFAULT_f64
.with_decimals(decimals)
.with_strip_trailing_zeros(false)
.format(value)
}

let epsilon = 16.0 * f32::EPSILON; // margin large enough to handle most peoples round-tripping needs

let min_decimals = *decimal_range.start();
let max_decimals = *decimal_range.end();
debug_assert!(min_decimals <= max_decimals);
debug_assert!(max_decimals < 100);
let max_decimals = max_decimals.at_most(16);
let min_decimals = min_decimals.at_most(max_decimals);

if min_decimals < max_decimals {
// Try using a few decimals as possible, and then add more until we have enough precision
// to round-trip the number.
for decimals in min_decimals..max_decimals {
let text = format_with_decimals(value, decimals);
if let Some(parsed) = re_format::parse_f64(&text) {
if egui::emath::almost_equal(parsed as f32, value as f32, epsilon) {
// Enough precision to show the value accurately - good!
return text;
}
}
}
// The value has more precision than we expected.
// Probably the value was set not by the slider, but from outside.
// In any case: show the full value
}

// Use max decimals
format_with_decimals(value, max_decimals)
}

/// Is this Ui in a resizable panel?
Expand Down
4 changes: 2 additions & 2 deletions crates/re_viewer/src/ui/memory_panel.rs
Original file line number Diff line number Diff line change
Expand Up @@ -474,8 +474,8 @@ impl MemoryPanel {
egui_plot::Plot::new("mem_history_plot")
.min_size(egui::Vec2::splat(200.0))
.label_formatter(|name, value| format!("{name}: {}", format_bytes(value.y)))
.x_axis_formatter(|time, _, _| format!("{} s", time.value))
.y_axis_formatter(|bytes, _, _| format_bytes(bytes.value))
.x_axis_formatter(|time, _| format!("{} s", time.value))
.y_axis_formatter(|bytes, _| format_bytes(bytes.value))
.show_x(false)
.legend(egui_plot::Legend::default().position(egui_plot::Corner::LeftTop))
.include_y(0.0)
Expand Down

0 comments on commit edb6ede

Please sign in to comment.