Improve choice of number of decimals to show when hovering in plot
This commit is contained in:
parent
0e2656b77c
commit
a68c891092
6 changed files with 149 additions and 125 deletions
|
@ -14,6 +14,7 @@ NOTE: [`epaint`](crates/epaint/CHANGELOG.md), [`eframe`](crates/eframe/CHANGELOG
|
||||||
|
|
||||||
### Changed 🔧
|
### Changed 🔧
|
||||||
* Improved plot grid appearance ([#2412](https://github.com/emilk/egui/pull/2412)).
|
* Improved plot grid appearance ([#2412](https://github.com/emilk/egui/pull/2412)).
|
||||||
|
* Improved the algorithm for picking the number of decimals to show when hovering values in the `Plot`.
|
||||||
|
|
||||||
### Fixed 🐛
|
### Fixed 🐛
|
||||||
* Expose `TextEdit`'s multiline flag to AccessKit ([#2448](https://github.com/emilk/egui/pull/2448)).
|
* Expose `TextEdit`'s multiline flag to AccessKit ([#2448](https://github.com/emilk/egui/pull/2448)).
|
||||||
|
|
|
@ -185,7 +185,11 @@ impl RectElement for Bar {
|
||||||
|
|
||||||
fn default_values_format(&self, transform: &ScreenTransform) -> String {
|
fn default_values_format(&self, transform: &ScreenTransform) -> String {
|
||||||
let scale = transform.dvalue_dpos();
|
let scale = transform.dvalue_dpos();
|
||||||
let y_decimals = ((-scale[1].abs().log10()).ceil().at_least(0.0) as usize).at_most(6);
|
let scale = match self.orientation {
|
||||||
format!("\n{:.*}", y_decimals, self.value)
|
Orientation::Horizontal => scale[0],
|
||||||
|
Orientation::Vertical => scale[1],
|
||||||
|
};
|
||||||
|
let decimals = ((-scale.abs().log10()).ceil().at_least(0.0) as usize).at_most(6);
|
||||||
|
crate::plot::format_number(self.value, decimals)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -269,9 +269,15 @@ impl RectElement for BoxElem {
|
||||||
|
|
||||||
fn default_values_format(&self, transform: &ScreenTransform) -> String {
|
fn default_values_format(&self, transform: &ScreenTransform) -> String {
|
||||||
let scale = transform.dvalue_dpos();
|
let scale = transform.dvalue_dpos();
|
||||||
let y_decimals = ((-scale[1].abs().log10()).ceil().at_least(0.0) as usize).at_most(6);
|
let scale = match self.orientation {
|
||||||
|
Orientation::Horizontal => scale[0],
|
||||||
|
Orientation::Vertical => scale[1],
|
||||||
|
};
|
||||||
|
let y_decimals = ((-scale.abs().log10()).ceil().at_least(0.0) as usize)
|
||||||
|
.at_most(6)
|
||||||
|
.at_least(1);
|
||||||
format!(
|
format!(
|
||||||
"\nMax = {max:.decimals$}\
|
"Max = {max:.decimals$}\
|
||||||
\nQuartile 3 = {q3:.decimals$}\
|
\nQuartile 3 = {q3:.decimals$}\
|
||||||
\nMedian = {med:.decimals$}\
|
\nMedian = {med:.decimals$}\
|
||||||
\nQuartile 1 = {q1:.decimals$}\
|
\nQuartile 1 = {q1:.decimals$}\
|
||||||
|
|
|
@ -1648,6 +1648,7 @@ fn add_rulers_and_text(
|
||||||
let mut text = elem.name().to_owned(); // could be empty
|
let mut text = elem.name().to_owned(); // could be empty
|
||||||
|
|
||||||
if show_values {
|
if show_values {
|
||||||
|
text.push('\n');
|
||||||
text.push_str(&elem.default_values_format(plot.transform));
|
text.push_str(&elem.default_values_format(plot.transform));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1694,8 +1695,8 @@ pub(super) fn rulers_at_value(
|
||||||
|
|
||||||
let text = {
|
let text = {
|
||||||
let scale = plot.transform.dvalue_dpos();
|
let scale = plot.transform.dvalue_dpos();
|
||||||
let x_decimals = ((-scale[0].abs().log10()).ceil().at_least(0.0) as usize).at_most(6);
|
let x_decimals = ((-scale[0].abs().log10()).ceil().at_least(0.0) as usize).clamp(1, 6);
|
||||||
let y_decimals = ((-scale[1].abs().log10()).ceil().at_least(0.0) as usize).at_most(6);
|
let y_decimals = ((-scale[1].abs().log10()).ceil().at_least(0.0) as usize).clamp(1, 6);
|
||||||
if let Some(custom_label) = label_formatter {
|
if let Some(custom_label) = label_formatter {
|
||||||
custom_label(name, &value)
|
custom_label(name, &value)
|
||||||
} else if plot.show_x && plot.show_y {
|
} else if plot.show_x && plot.show_y {
|
||||||
|
|
|
@ -1647,3 +1647,16 @@ fn fill_marks_between(out: &mut Vec<GridMark>, step_size: f64, (min, max): (f64,
|
||||||
});
|
});
|
||||||
out.extend(marks_iter);
|
out.extend(marks_iter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper for formatting a number so that we always show at least a few decimals,
|
||||||
|
/// unless it is an integer, in which case we never show any decimals.
|
||||||
|
pub fn format_number(number: f64, num_decimals: usize) -> String {
|
||||||
|
let is_integral = number as i64 as f64 == number;
|
||||||
|
if is_integral {
|
||||||
|
// perfect integer - show it as such:
|
||||||
|
format!("{:.0}", number)
|
||||||
|
} else {
|
||||||
|
// make sure we tell the user it is not an integer by always showing a decimal or two:
|
||||||
|
format!("{:.*}", num_decimals.at_least(1), number)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -11,6 +11,124 @@ use plot::{
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
enum Panel {
|
||||||
|
Lines,
|
||||||
|
Markers,
|
||||||
|
Legend,
|
||||||
|
Charts,
|
||||||
|
Items,
|
||||||
|
Interaction,
|
||||||
|
CustomAxes,
|
||||||
|
LinkedAxes,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Panel {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Lines
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[derive(PartialEq, Default)]
|
||||||
|
pub struct PlotDemo {
|
||||||
|
line_demo: LineDemo,
|
||||||
|
marker_demo: MarkerDemo,
|
||||||
|
legend_demo: LegendDemo,
|
||||||
|
charts_demo: ChartsDemo,
|
||||||
|
items_demo: ItemsDemo,
|
||||||
|
interaction_demo: InteractionDemo,
|
||||||
|
custom_axes_demo: CustomAxisDemo,
|
||||||
|
linked_axes_demo: LinkedAxisDemo,
|
||||||
|
open_panel: Panel,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::Demo for PlotDemo {
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"🗠 Plot"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn show(&mut self, ctx: &Context, open: &mut bool) {
|
||||||
|
use super::View as _;
|
||||||
|
Window::new(self.name())
|
||||||
|
.open(open)
|
||||||
|
.default_size(vec2(400.0, 400.0))
|
||||||
|
.vscroll(false)
|
||||||
|
.show(ctx, |ui| self.ui(ui));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl super::View for PlotDemo {
|
||||||
|
fn ui(&mut self, ui: &mut Ui) {
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
egui::reset_button(ui, self);
|
||||||
|
ui.collapsing("Instructions", |ui| {
|
||||||
|
ui.label("Pan by dragging, or scroll (+ shift = horizontal).");
|
||||||
|
ui.label("Box zooming: Right click to zoom in and zoom out using a selection.");
|
||||||
|
if cfg!(target_arch = "wasm32") {
|
||||||
|
ui.label("Zoom with ctrl / ⌘ + pointer wheel, or with pinch gesture.");
|
||||||
|
} else if cfg!(target_os = "macos") {
|
||||||
|
ui.label("Zoom with ctrl / ⌘ + scroll.");
|
||||||
|
} else {
|
||||||
|
ui.label("Zoom with ctrl + scroll.");
|
||||||
|
}
|
||||||
|
ui.label("Reset view with double-click.");
|
||||||
|
ui.add(crate::egui_github_link_file!());
|
||||||
|
});
|
||||||
|
});
|
||||||
|
ui.separator();
|
||||||
|
ui.horizontal(|ui| {
|
||||||
|
ui.selectable_value(&mut self.open_panel, Panel::Lines, "Lines");
|
||||||
|
ui.selectable_value(&mut self.open_panel, Panel::Markers, "Markers");
|
||||||
|
ui.selectable_value(&mut self.open_panel, Panel::Legend, "Legend");
|
||||||
|
ui.selectable_value(&mut self.open_panel, Panel::Charts, "Charts");
|
||||||
|
ui.selectable_value(&mut self.open_panel, Panel::Items, "Items");
|
||||||
|
ui.selectable_value(&mut self.open_panel, Panel::Interaction, "Interaction");
|
||||||
|
ui.selectable_value(&mut self.open_panel, Panel::CustomAxes, "Custom Axes");
|
||||||
|
ui.selectable_value(&mut self.open_panel, Panel::LinkedAxes, "Linked Axes");
|
||||||
|
});
|
||||||
|
ui.separator();
|
||||||
|
|
||||||
|
match self.open_panel {
|
||||||
|
Panel::Lines => {
|
||||||
|
self.line_demo.ui(ui);
|
||||||
|
}
|
||||||
|
Panel::Markers => {
|
||||||
|
self.marker_demo.ui(ui);
|
||||||
|
}
|
||||||
|
Panel::Legend => {
|
||||||
|
self.legend_demo.ui(ui);
|
||||||
|
}
|
||||||
|
Panel::Charts => {
|
||||||
|
self.charts_demo.ui(ui);
|
||||||
|
}
|
||||||
|
Panel::Items => {
|
||||||
|
self.items_demo.ui(ui);
|
||||||
|
}
|
||||||
|
Panel::Interaction => {
|
||||||
|
self.interaction_demo.ui(ui);
|
||||||
|
}
|
||||||
|
Panel::CustomAxes => {
|
||||||
|
self.custom_axes_demo.ui(ui);
|
||||||
|
}
|
||||||
|
Panel::LinkedAxes => {
|
||||||
|
self.linked_axes_demo.ui(ui);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_approx_zero(val: f64) -> bool {
|
||||||
|
val.abs() < 1e-6
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_approx_integer(val: f64) -> bool {
|
||||||
|
val.fract().abs() < 1e-6
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
#[derive(PartialEq)]
|
#[derive(PartialEq)]
|
||||||
struct LineDemo {
|
struct LineDemo {
|
||||||
animate: bool,
|
animate: bool,
|
||||||
|
@ -754,7 +872,6 @@ impl ChartsDemo {
|
||||||
|
|
||||||
Plot::new("Normal Distribution Demo")
|
Plot::new("Normal Distribution Demo")
|
||||||
.legend(Legend::default())
|
.legend(Legend::default())
|
||||||
.data_aspect(1.0)
|
|
||||||
.clamp_grid(true)
|
.clamp_grid(true)
|
||||||
.show(ui, |plot_ui| plot_ui.bar_chart(chart))
|
.show(ui, |plot_ui| plot_ui.bar_chart(chart))
|
||||||
.response
|
.response
|
||||||
|
@ -865,121 +982,3 @@ impl ChartsDemo {
|
||||||
.response
|
.response
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
|
||||||
enum Panel {
|
|
||||||
Lines,
|
|
||||||
Markers,
|
|
||||||
Legend,
|
|
||||||
Charts,
|
|
||||||
Items,
|
|
||||||
Interaction,
|
|
||||||
CustomAxes,
|
|
||||||
LinkedAxes,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Panel {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::Lines
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
|
|
||||||
#[derive(PartialEq, Default)]
|
|
||||||
pub struct PlotDemo {
|
|
||||||
line_demo: LineDemo,
|
|
||||||
marker_demo: MarkerDemo,
|
|
||||||
legend_demo: LegendDemo,
|
|
||||||
charts_demo: ChartsDemo,
|
|
||||||
items_demo: ItemsDemo,
|
|
||||||
interaction_demo: InteractionDemo,
|
|
||||||
custom_axes_demo: CustomAxisDemo,
|
|
||||||
linked_axes_demo: LinkedAxisDemo,
|
|
||||||
open_panel: Panel,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl super::Demo for PlotDemo {
|
|
||||||
fn name(&self) -> &'static str {
|
|
||||||
"🗠 Plot"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn show(&mut self, ctx: &Context, open: &mut bool) {
|
|
||||||
use super::View as _;
|
|
||||||
Window::new(self.name())
|
|
||||||
.open(open)
|
|
||||||
.default_size(vec2(400.0, 400.0))
|
|
||||||
.vscroll(false)
|
|
||||||
.show(ctx, |ui| self.ui(ui));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl super::View for PlotDemo {
|
|
||||||
fn ui(&mut self, ui: &mut Ui) {
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
egui::reset_button(ui, self);
|
|
||||||
ui.collapsing("Instructions", |ui| {
|
|
||||||
ui.label("Pan by dragging, or scroll (+ shift = horizontal).");
|
|
||||||
ui.label("Box zooming: Right click to zoom in and zoom out using a selection.");
|
|
||||||
if cfg!(target_arch = "wasm32") {
|
|
||||||
ui.label("Zoom with ctrl / ⌘ + pointer wheel, or with pinch gesture.");
|
|
||||||
} else if cfg!(target_os = "macos") {
|
|
||||||
ui.label("Zoom with ctrl / ⌘ + scroll.");
|
|
||||||
} else {
|
|
||||||
ui.label("Zoom with ctrl + scroll.");
|
|
||||||
}
|
|
||||||
ui.label("Reset view with double-click.");
|
|
||||||
ui.add(crate::egui_github_link_file!());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
ui.separator();
|
|
||||||
ui.horizontal(|ui| {
|
|
||||||
ui.selectable_value(&mut self.open_panel, Panel::Lines, "Lines");
|
|
||||||
ui.selectable_value(&mut self.open_panel, Panel::Markers, "Markers");
|
|
||||||
ui.selectable_value(&mut self.open_panel, Panel::Legend, "Legend");
|
|
||||||
ui.selectable_value(&mut self.open_panel, Panel::Charts, "Charts");
|
|
||||||
ui.selectable_value(&mut self.open_panel, Panel::Items, "Items");
|
|
||||||
ui.selectable_value(&mut self.open_panel, Panel::Interaction, "Interaction");
|
|
||||||
ui.selectable_value(&mut self.open_panel, Panel::CustomAxes, "Custom Axes");
|
|
||||||
ui.selectable_value(&mut self.open_panel, Panel::LinkedAxes, "Linked Axes");
|
|
||||||
});
|
|
||||||
ui.separator();
|
|
||||||
|
|
||||||
match self.open_panel {
|
|
||||||
Panel::Lines => {
|
|
||||||
self.line_demo.ui(ui);
|
|
||||||
}
|
|
||||||
Panel::Markers => {
|
|
||||||
self.marker_demo.ui(ui);
|
|
||||||
}
|
|
||||||
Panel::Legend => {
|
|
||||||
self.legend_demo.ui(ui);
|
|
||||||
}
|
|
||||||
Panel::Charts => {
|
|
||||||
self.charts_demo.ui(ui);
|
|
||||||
}
|
|
||||||
Panel::Items => {
|
|
||||||
self.items_demo.ui(ui);
|
|
||||||
}
|
|
||||||
Panel::Interaction => {
|
|
||||||
self.interaction_demo.ui(ui);
|
|
||||||
}
|
|
||||||
Panel::CustomAxes => {
|
|
||||||
self.custom_axes_demo.ui(ui);
|
|
||||||
}
|
|
||||||
Panel::LinkedAxes => {
|
|
||||||
self.linked_axes_demo.ui(ui);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_approx_zero(val: f64) -> bool {
|
|
||||||
val.abs() < 1e-6
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_approx_integer(val: f64) -> bool {
|
|
||||||
val.fract().abs() < 1e-6
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue