1
0
mirror of https://github.com/emilk/egui.git synced 2026-08-31 22:00:03 -04:00

Draw axis labels and ticks outside of plotting window (#2284)

* Always draw axis labels at plot borders

* Revert "Always draw axis labels at plot borders"

This reverts commit 9235e6603366d3b8a8189e2a5fc28c9780b7f54f.

* Add axis labels for plots

* First Draft of axis labels outside of plotting window

* plot: Tick placement of opposite axes and digit constraints

* plot: Axis label API

* plot: Update demo lib

* plot: resolve clippy warning

* Update changelog

* Remove default axis

* Fix clippy

* plot: Remove unused comments

* plot-axis: Rebase label opacity calculation on master

* plot: Resolve check.sh warnings

* plot-axis: Use 'into impl<WidgetText>' as axis label formatter

* plot-axis: Expose more conveniece functions to public API. Add axis labels to demo app

* plot-axes: Resolve ./scripts/check.sh warnings

* typo in comment

* Use `TAU` instead of the legacy `PI`

* Simpler generic syntax

* Use `Arc` to avoid some expensive clones

* Use `Margin` instead of a,b,c,d

* Add some vertical spacing

* De-duplicate color_from_contrast

* better naming

* Fix typos

* cnt -> num

* Axis are present by default, with empty names

* Add HPlacement and VPlacement

* Don't catch clicks and drags on axes

* Remove generics to minimize monomorphization code bloat

* Create helper function

* Remove changelog entry

* Simplify more

---------

Co-authored-by: Emil Ernerfeldt <emil.ernerfeldt@gmail.com>
This commit is contained in:
JohannesProgrammiert
2023-08-14 17:51:17 +02:00
committed by GitHub
parent a3ae81cadb
commit dbe55ba46a
7 changed files with 807 additions and 202 deletions

View File

@@ -1,12 +1,12 @@
use std::f64::consts::TAU;
use std::ops::RangeInclusive;
use egui::plot::{AxisBools, GridInput, GridMark, PlotResponse};
use egui::*;
use plot::{
Arrows, Bar, BarChart, BoxElem, BoxPlot, BoxSpread, CoordinatesFormatter, Corner, HLine,
Legend, Line, LineStyle, MarkerShape, Plot, PlotImage, PlotPoint, PlotPoints, Points, Polygon,
Text, VLine,
use egui::plot::{
Arrows, AxisBools, AxisHints, Bar, BarChart, BoxElem, BoxPlot, BoxSpread, CoordinatesFormatter,
Corner, GridInput, GridMark, HLine, Legend, Line, LineStyle, MarkerShape, Plot, PlotImage,
PlotPoint, PlotPoints, PlotResponse, Points, Polygon, Text, VLine,
};
// ----------------------------------------------------------------------------
@@ -119,17 +119,9 @@ impl super::View for PlotDemo {
}
}
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(Copy, Clone, PartialEq)]
struct LineDemo {
animate: bool,
time: f64,
@@ -138,6 +130,8 @@ struct LineDemo {
square: bool,
proportional: bool,
coordinates: bool,
show_axes: bool,
show_grid: bool,
line_style: LineStyle,
}
@@ -151,6 +145,8 @@ impl Default for LineDemo {
square: false,
proportional: true,
coordinates: true,
show_axes: true,
show_grid: true,
line_style: LineStyle::Solid,
}
}
@@ -165,9 +161,10 @@ impl LineDemo {
circle_center,
square,
proportional,
line_style,
coordinates,
..
show_axes,
show_grid,
line_style,
} = self;
ui.horizontal(|ui| {
@@ -195,6 +192,13 @@ impl LineDemo {
});
});
ui.vertical(|ui| {
ui.checkbox(show_axes, "Show axes");
ui.checkbox(show_grid, "Show grid");
ui.checkbox(coordinates, "Show coordinates on hover")
.on_hover_text("Can take a custom formatting function.");
});
ui.vertical(|ui| {
ui.style_mut().wrap = Some(false);
ui.checkbox(animate, "Animate");
@@ -202,8 +206,6 @@ impl LineDemo {
.on_hover_text("Always keep the viewport square.");
ui.checkbox(proportional, "Proportional data axes")
.on_hover_text("Tick are the same size on both axes.");
ui.checkbox(coordinates, "Show coordinates")
.on_hover_text("Can take a custom formatting function.");
ComboBox::from_label("Line style")
.selected_text(line_style.to_string())
@@ -268,11 +270,16 @@ impl LineDemo {
impl LineDemo {
fn ui(&mut self, ui: &mut Ui) -> Response {
self.options_ui(ui);
if self.animate {
ui.ctx().request_repaint();
self.time += ui.input(|i| i.unstable_dt).at_most(1.0 / 30.0) as f64;
};
let mut plot = Plot::new("lines_demo").legend(Legend::default());
let mut plot = Plot::new("lines_demo")
.legend(Legend::default())
.y_axis_width(4)
.show_axes(self.show_axes)
.show_grid(self.show_grid);
if self.square {
plot = plot.view_aspect(1.0);
}
@@ -429,8 +436,8 @@ impl LegendDemo {
);
ui.end_row();
});
let legend_plot = Plot::new("legend_demo")
.y_axis_width(2)
.legend(config.clone())
.data_aspect(1.0);
legend_plot
@@ -523,7 +530,7 @@ impl CustomAxesDemo {
100.0 * y
}
let x_fmt = |x, _range: &RangeInclusive<f64>| {
let x_fmt = |x, _digits, _range: &RangeInclusive<f64>| {
if x < 0.0 * MINS_PER_DAY || x >= 5.0 * MINS_PER_DAY {
// No labels outside value bounds
String::new()
@@ -536,7 +543,7 @@ impl CustomAxesDemo {
}
};
let y_fmt = |y, _range: &RangeInclusive<f64>| {
let y_fmt = |y, _digits, _range: &RangeInclusive<f64>| {
// Display only integer percentages
if !is_approx_zero(y) && is_approx_integer(100.0 * y) {
format!("{:.0}%", percent(y))
@@ -557,10 +564,23 @@ impl CustomAxesDemo {
ui.label("Zoom in on the X-axis to see hours and minutes");
let x_axes = vec![
AxisHints::default().label("Time").formatter(x_fmt),
AxisHints::default().label("Value"),
];
let y_axes = vec![
AxisHints::default()
.label("Percent")
.formatter(y_fmt)
.max_digits(4),
AxisHints::default()
.label("Absolute")
.placement(plot::HPlacement::Right),
];
Plot::new("custom_axes")
.data_aspect(2.0 * MINS_PER_DAY as f32)
.x_axis_formatter(x_fmt)
.y_axis_formatter(y_fmt)
.custom_x_axes(x_axes)
.custom_y_axes(y_axes)
.x_grid_spacer(CustomAxesDemo::x_grid)
.label_formatter(label_fmt)
.show(ui, |plot_ui| {
@@ -582,15 +602,11 @@ struct LinkedAxesDemo {
impl Default for LinkedAxesDemo {
fn default() -> Self {
let link_x = true;
let link_y = false;
let link_cursor_x = true;
let link_cursor_y = false;
Self {
link_x,
link_y,
link_cursor_x,
link_cursor_y,
link_x: true,
link_y: true,
link_cursor_x: true,
link_cursor_y: true,
}
}
}
@@ -642,25 +658,29 @@ impl LinkedAxesDemo {
let link_group_id = ui.id().with("linked_demo");
ui.horizontal(|ui| {
Plot::new("linked_axis_1")
Plot::new("left-top")
.data_aspect(1.0)
.width(250.0)
.height(250.0)
.link_axis(link_group_id, self.link_x, self.link_y)
.link_cursor(link_group_id, self.link_cursor_x, self.link_cursor_y)
.show(ui, LinkedAxesDemo::configure_plot);
Plot::new("linked_axis_2")
Plot::new("right-top")
.data_aspect(2.0)
.width(150.0)
.height(250.0)
.y_axis_width(3)
.y_axis_label("y")
.y_axis_position(plot::HPlacement::Right)
.link_axis(link_group_id, self.link_x, self.link_y)
.link_cursor(link_group_id, self.link_cursor_x, self.link_cursor_y)
.show(ui, LinkedAxesDemo::configure_plot);
});
Plot::new("linked_axis_3")
Plot::new("left-bottom")
.data_aspect(0.5)
.width(250.0)
.height(150.0)
.x_axis_label("x")
.link_axis(link_group_id, self.link_x, self.link_y)
.link_cursor(link_group_id, self.link_cursor_x, self.link_cursor_y)
.show(ui, LinkedAxesDemo::configure_plot)
@@ -889,6 +909,7 @@ impl ChartsDemo {
Plot::new("Normal Distribution Demo")
.legend(Legend::default())
.clamp_grid(true)
.y_axis_width(3)
.allow_zoom(self.allow_zoom)
.allow_drag(self.allow_drag)
.show(ui, |plot_ui| plot_ui.bar_chart(chart))
@@ -1003,3 +1024,11 @@ impl ChartsDemo {
.response
}
}
fn is_approx_zero(val: f64) -> bool {
val.abs() < 1e-6
}
fn is_approx_integer(val: f64) -> bool {
val.fract().abs() < 1e-6
}

View File

@@ -271,6 +271,7 @@ fn example_plot(ui: &mut egui::Ui) -> egui::Response {
let line = Line::new(line_points);
egui::plot::Plot::new("example_plot")
.height(32.0)
.show_axes(false)
.data_aspect(1.0)
.show(ui, |plot_ui| plot_ui.line(line))
.response