Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion egui_plot/src/items/bar_chart.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ impl PlotItem for BarChart {
shapes: &mut Vec<Shape>,
cursors: &mut Vec<Cursor>,
plot: &PlotConfig<'_>,
_: &LabelFormatter<'_>,
_: &Option<LabelFormatter<'_>>,
) {
let bar = &self.bars[elem.index];

Expand Down
2 changes: 1 addition & 1 deletion egui_plot/src/items/box_plot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ impl PlotItem for BoxPlot {
shapes: &mut Vec<Shape>,
cursors: &mut Vec<Cursor>,
plot: &PlotConfig<'_>,
_: &LabelFormatter<'_>,
_: &Option<LabelFormatter<'_>>,
) {
let box_plot = &self.boxes[elem.index];

Expand Down
2 changes: 1 addition & 1 deletion egui_plot/src/items/heatmap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -460,7 +460,7 @@ impl PlotItem for Heatmap {
shapes: &mut Vec<Shape>,
_cursors: &mut Vec<Cursor>,
plot: &PlotConfig<'_>,
_: &LabelFormatter<'_>,
_: &Option<LabelFormatter<'_>>,
) {
let (rect, color, text) = self.tile_view_info(plot.ui, plot.transform, elem.index);
let mut mesh = Mesh::default();
Expand Down
56 changes: 21 additions & 35 deletions egui_plot/src/items/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ use std::ops::RangeInclusive;
use egui::Align2;
use egui::Color32;
use egui::Id;
use egui::NumExt as _;
use egui::PopupAnchor;
use egui::Pos2;
use egui::Shape;
Expand Down Expand Up @@ -92,6 +91,9 @@ pub struct PlotConfig<'a> {

/// Whether to show the y-axis value.
pub show_y: bool,

/// Whether to show the crosshair rulers.
pub show_crosshair: bool,
}

/// Trait shared by things that can be drawn in the plot.
Expand Down Expand Up @@ -172,7 +174,7 @@ pub trait PlotItem {
shapes: &mut Vec<Shape>,
cursors: &mut Vec<Cursor>,
plot: &PlotConfig<'_>,
label_formatter: &LabelFormatter<'_>,
label_formatter: &Option<LabelFormatter<'_>>,
) {
let points = match self.geometry() {
PlotGeometry::Points(points) => points,
Expand Down Expand Up @@ -273,44 +275,28 @@ pub(super) fn rulers_and_tooltip_at_value(
name: &str,
plot: &PlotConfig<'_>,
cursors: &mut Vec<Cursor>,
label_formatter: &LabelFormatter<'_>,
label_formatter: &Option<LabelFormatter<'_>>,
) {
if plot.show_x {
cursors.push(Cursor::Vertical { x: value.x });
}
if plot.show_y {
cursors.push(Cursor::Horizontal { y: value.y });
}

let text = if let Some(custom_label) = label_formatter {
let label = custom_label(name, &value);
if label.is_empty() {
return;
// Add crosshair rulers if enabled
if plot.show_crosshair {
if plot.show_x {
cursors.push(Cursor::Vertical { x: value.x });
}
label
} else {
let prefix = if name.is_empty() {
String::new()
} else {
format!("{name}\n")
};
let scale = plot.transform.dvalue_dpos();
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).clamp(1, 6);
if plot.show_x && plot.show_y {
format!(
"{}x = {:.*}\ny = {:.*}",
prefix, x_decimals, value.x, y_decimals, value.y
)
} else if plot.show_x {
format!("{}x = {:.*}", prefix, x_decimals, value.x)
} else if plot.show_y {
format!("{}y = {:.*}", prefix, y_decimals, value.y)
} else {
unreachable!()
if plot.show_y {
cursors.push(Cursor::Horizontal { y: value.y });
}
}

// Only show tooltip if label_formatter is provided
let Some(custom_label) = label_formatter else {
return;
};

let text = custom_label(name, &value);
if text.is_empty() {
return;
}

// We show the tooltip as soon as we're hovering the plot area:
let mut tooltip = egui::Tooltip::always_open(
plot_area_response.ctx.clone(),
Expand Down
13 changes: 12 additions & 1 deletion egui_plot/src/label.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,15 @@ pub fn format_number(number: f64, num_decimals: usize) -> String {
type LabelFormatterFn<'a> = dyn Fn(&str, &PlotPoint) -> String + 'a;

/// Optional label formatter function for customizing hover labels.
pub type LabelFormatter<'a> = Option<Box<LabelFormatterFn<'a>>>;
pub type LabelFormatter<'a> = Box<LabelFormatterFn<'a>>;

/// Default label formatter that shows the x and y coordinates with 3 decimal
/// places.
pub fn default_label_formatter(name: &str, value: &PlotPoint) -> String {
let prefix = if name.is_empty() {
String::new()
} else {
format!("{name}\n")
};
format!("{}x = {:.3}\ny = {:.3}", prefix, value.x, value.y)
}
1 change: 1 addition & 0 deletions egui_plot/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ pub use crate::items::Span;
pub use crate::items::Text;
pub use crate::items::VLine;
pub use crate::label::LabelFormatter;
pub use crate::label::default_label_formatter;
pub use crate::label::format_number;
pub use crate::memory::PlotMemory;
pub use crate::overlays::ColorConflictHandling;
Expand Down
12 changes: 11 additions & 1 deletion egui_plot/src/plot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,8 @@ pub struct Plot<'a> {

show_x: bool,
show_y: bool,
label_formatter: LabelFormatter<'a>,
show_crosshair: bool,
label_formatter: Option<LabelFormatter<'a>>,
coordinates_formatter: Option<(Corner, CoordinatesFormatter<'a>)>,
x_axes: Vec<AxisHints<'a>>, // default x axes
y_axes: Vec<AxisHints<'a>>, // default y axes
Expand Down Expand Up @@ -166,6 +167,7 @@ impl<'a> Plot<'a> {

show_x: true,
show_y: true,
show_crosshair: true,
label_formatter: None,
coordinates_formatter: None,
x_axes: vec![AxisHints::new(Axis::X)],
Expand Down Expand Up @@ -272,6 +274,13 @@ impl<'a> Plot<'a> {
self
}

/// Show the crosshair when hovering. Default: `true`.
#[inline]
pub fn show_crosshair(mut self, show: bool) -> Self {
self.show_crosshair = show;
self
}

/// Always keep the X-axis centered. Default: `false`.
#[inline]
pub fn center_x_axis(mut self, on: bool) -> Self {
Expand Down Expand Up @@ -1544,6 +1553,7 @@ impl<'a> Plot<'a> {
transform,
show_x: show_xy.x,
show_y: show_xy.y,
show_crosshair: self.show_crosshair,
};

let mut cursors = Vec::new();
Expand Down
37 changes: 18 additions & 19 deletions examples/lines/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use egui_plot::Line;
use egui_plot::LineStyle;
use egui_plot::Plot;
use egui_plot::PlotPoints;
use egui_plot::default_label_formatter;

#[derive(Clone, Copy, PartialEq)]
pub struct LineExample {
Expand All @@ -27,6 +28,8 @@ pub struct LineExample {
coordinates: bool,
show_axes: bool,
show_grid: bool,
show_crosshair: bool,
show_labels: bool,
line_style: LineStyle,
gradient: bool,
gradient_fill: bool,
Expand All @@ -46,6 +49,8 @@ impl Default for LineExample {
coordinates: true,
show_axes: true,
show_grid: true,
show_crosshair: true,
show_labels: true,
line_style: LineStyle::Solid,
gradient: false,
gradient_fill: false,
Expand All @@ -61,31 +66,25 @@ impl LineExample {
ui.group(|ui| {
ui.vertical(|ui| {
ui.label("Circle:");
ui.add(
egui::DragValue::new(&mut self.circle_radius)
.speed(0.1)
.range(0.0..=f64::INFINITY)
.prefix("r: "),
);
ui.add(egui::DragValue::new(&mut self.circle_radius).speed(0.1).prefix("r: "));
ui.horizontal(|ui| {
ui.add(egui::DragValue::new(&mut self.circle_center.x).speed(0.1).prefix("x: "));
ui.add(egui::DragValue::new(&mut self.circle_center.y).speed(1.0).prefix("y: "));
});
})
});
});
ui.vertical(|ui| {
ui.checkbox(&mut self.show_axes, "Show axes");
ui.checkbox(&mut self.show_grid, "Show grid");
ui.checkbox(&mut self.coordinates, "Show coordinates on hover")
.on_hover_text("Can take a custom formatting function.");
ui.checkbox(&mut self.show_crosshair, "Show crosshair");
ui.checkbox(&mut self.coordinates, "Show coordinates");
ui.checkbox(&mut self.show_labels, "Show hover labels");
});
ui.vertical(|ui| {
ui.style_mut().wrap_mode = Some(TextWrapMode::Extend);
ui.checkbox(&mut self.animate, "Animate");
ui.checkbox(&mut self.square, "Square view")
.on_hover_text("Always keep the viewport square.");
ui.checkbox(&mut self.proportional, "Proportional data axes")
.on_hover_text("Tick are the same size on both axes.");
ui.checkbox(&mut self.square, "Square view");
ui.checkbox(&mut self.proportional, "Proportional data axes");
ComboBox::from_label("Line style")
.selected_text(self.line_style.to_string())
.show_ui(ui, |ui| {
Expand All @@ -103,8 +102,6 @@ impl LineExample {
ui.vertical(|ui| {
ui.checkbox(&mut self.gradient, "Gradient line");
ui.add_enabled(self.gradient, Checkbox::new(&mut self.gradient_fill, "Gradient fill"));
});
ui.vertical(|ui| {
ui.checkbox(&mut self.invert_x, "Invert X axis");
ui.checkbox(&mut self.invert_y, "Invert Y axis");
});
Expand All @@ -113,10 +110,9 @@ impl LineExample {
}

fn circle(&self) -> Line<'_> {
let n = 512;
let points: PlotPoints<'_> = (0..=n)
let points: PlotPoints<'_> = (0..=512)
.map(|i| {
let t = egui::remap(i as f64, 0.0..=(n as f64), 0.0..=TAU);
let t = egui::remap(i as f64, 0.0..=512.0, 0.0..=TAU);
[
self.circle_radius * t.cos() + self.circle_center.x as f64,
self.circle_radius * t.sin() + self.circle_center.y as f64,
Expand Down Expand Up @@ -166,11 +162,11 @@ impl LineExample {
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().title("Lines"))
.show_axes(self.show_axes)
.show_grid(self.show_grid)
.show_crosshair(self.show_crosshair)
.invert_x(self.invert_x)
.invert_y(self.invert_y);
if self.square {
Expand All @@ -182,6 +178,9 @@ impl LineExample {
if self.coordinates {
plot = plot.coordinates_formatter(Corner::LeftBottom, CoordinatesFormatter::default());
}
if self.show_labels {
plot = plot.label_formatter(default_label_formatter);
}
plot.show(ui, |plot_ui| {
plot_ui.line(self.circle());
plot_ui.line(self.sin());
Expand Down
Loading