Skip to content

Commit

Permalink
Format numbers in DragValues using re_format
Browse files Browse the repository at this point in the history
  • Loading branch information
emilk committed Jun 30, 2024
1 parent ca0a7aa commit 9c4df0b
Show file tree
Hide file tree
Showing 2 changed files with 59 additions and 0 deletions.
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
45 changes: 45 additions & 0 deletions crates/re_ui/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,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.min(16);
let min_decimals = min_decimals.min(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

0 comments on commit 9c4df0b

Please sign in to comment.