From 5222061bdab8387e26301f7eda046d828d09c18a Mon Sep 17 00:00:00 2001 From: Nicolas Date: Sat, 8 Feb 2025 00:59:16 +0100 Subject: [PATCH 1/2] experiment with id and hashmap for userdata in plot --- demo/src/plot_demo.rs | 65 +++++++++++++++++++++++++++++++++-- egui_plot/src/items/mod.rs | 18 +++++----- egui_plot/src/items/values.rs | 4 +-- egui_plot/src/lib.rs | 7 ++-- 4 files changed, 79 insertions(+), 15 deletions(-) diff --git a/demo/src/plot_demo.rs b/demo/src/plot_demo.rs index 7acbc54..1ef6517 100644 --- a/demo/src/plot_demo.rs +++ b/demo/src/plot_demo.rs @@ -2,7 +2,7 @@ use std::f64::consts::TAU; use std::ops::RangeInclusive; use egui::{ - remap, vec2, Color32, ComboBox, NumExt, Pos2, Response, ScrollArea, Stroke, TextWrapMode, + remap, vec2, Color32, ComboBox, Id, NumExt, Pos2, Response, ScrollArea, Stroke, TextWrapMode, Vec2b, WidgetInfo, WidgetType, }; @@ -24,6 +24,7 @@ enum Panel { Interaction, CustomAxes, LinkedAxes, + Userdata, } impl Default for Panel { @@ -44,6 +45,7 @@ pub struct PlotDemo { interaction_demo: InteractionDemo, custom_axes_demo: CustomAxesDemo, linked_axes_demo: LinkedAxesDemo, + userdata_demo: UserdataDemo, open_panel: Panel, } @@ -88,6 +90,7 @@ impl PlotDemo { 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.selectable_value(&mut self.open_panel, Panel::Userdata, "Userdata"); }); }); ui.separator(); @@ -117,6 +120,9 @@ impl PlotDemo { Panel::LinkedAxes => { self.linked_axes_demo.ui(ui); } + Panel::Userdata => { + self.userdata_demo.ui(ui); + } } } } @@ -565,7 +571,7 @@ impl CustomAxesDemo { } }; - let label_fmt = |_s: &str, val: &PlotPoint| { + let label_fmt = |_s: &str, val: &PlotPoint, _| { format!( "Day {d}, {h}:{m:02}\n{p:.2}%", d = day(val.x), @@ -1101,6 +1107,61 @@ impl ChartsDemo { } } +#[derive(PartialEq, serde::Deserialize, serde::Serialize, Default)] +struct UserdataDemo {} + +struct DemoPoint { + x: f64, + y: f64, + custom_thing: bool, +} + +impl UserdataDemo { + #[allow(clippy::unused_self, clippy::significant_drop_tightening)] + fn ui(&self, ui: &mut egui::Ui) -> Response { + let points = (1..=1000) + .map(|i| DemoPoint { + x: i as f64 / 1000.0, + y: ((i as f64) / 1000.0 * std::f64::consts::PI * 2.0).sin(), + custom_thing: i % 2 == 0, + }) + .collect::>(); + + let custom_things = + std::sync::Arc::new(egui::mutex::Mutex::new(std::collections::HashMap::< + Id, + Vec, + >::new())); + + let custom_things_ = custom_things.clone(); + Plot::new("Userdata Plot Demo") + .legend(Legend::default()) + .label_formatter(|_, _, item| { + format!( + "item: {:?}\ncustom_thing: {:?}", + item, + item.and_then(|(id, index)| custom_things_ + .lock() + .get(&id) + .and_then(|vec| vec.get(index).copied())) + ) + }) + .show(ui, |plot_ui| { + let id = Id::new(1234); + let mut lock = custom_things.lock(); + let entry = lock.entry(id).or_default(); + + for p in &points { + entry.push(p.custom_thing); + } + + plot_ui + .line(Line::new(points.iter().map(|p| [p.x, p.y]).collect::>()).id(id)); + }) + .response + } +} + fn is_approx_zero(val: f64) -> bool { val.abs() < 1e-6 } diff --git a/egui_plot/src/items/mod.rs b/egui_plot/src/items/mod.rs index 68d83a7..2bdd586 100644 --- a/egui_plot/src/items/mod.rs +++ b/egui_plot/src/items/mod.rs @@ -64,7 +64,7 @@ pub trait PlotItem { match self.geometry() { PlotGeometry::None => None, - PlotGeometry::Points(points) => points + PlotGeometry::Points(points, _) => points .iter() .enumerate() .map(|(index, value)| { @@ -88,8 +88,8 @@ pub trait PlotItem { plot: &PlotConfig<'_>, label_formatter: &LabelFormatter<'_>, ) { - let points = match self.geometry() { - PlotGeometry::Points(points) => points, + let (points, id) = match self.geometry() { + PlotGeometry::Points(points, id) => (points, id), PlotGeometry::None => { panic!("If the PlotItem has no geometry, on_hover() must not be called") } @@ -112,6 +112,7 @@ pub trait PlotItem { rulers_at_value( pointer, value, + id.map(|id| (id, elem.index)), self.name(), plot, shapes, @@ -606,7 +607,7 @@ impl<'a> PlotItem for Line<'a> { } fn geometry(&self) -> PlotGeometry<'_> { - PlotGeometry::Points(self.series.points()) + PlotGeometry::Points(self.series.points(), self.id()) } fn bounds(&self) -> PlotBounds { @@ -762,7 +763,7 @@ impl<'a> PlotItem for Polygon<'a> { } fn geometry(&self) -> PlotGeometry<'_> { - PlotGeometry::Points(self.series.points()) + PlotGeometry::Points(self.series.points(), self.id()) } fn bounds(&self) -> PlotBounds { @@ -1183,7 +1184,7 @@ impl<'a> PlotItem for Points<'a> { } fn geometry(&self) -> PlotGeometry<'_> { - PlotGeometry::Points(self.series.points()) + PlotGeometry::Points(self.series.points(), self.id()) } fn bounds(&self) -> PlotBounds { @@ -1340,7 +1341,7 @@ impl<'a> PlotItem for Arrows<'a> { } fn geometry(&self) -> PlotGeometry<'_> { - PlotGeometry::Points(self.origins.points()) + PlotGeometry::Points(self.origins.points(), self.id()) } fn bounds(&self) -> PlotBounds { @@ -2040,6 +2041,7 @@ fn add_rulers_and_text( pub(super) fn rulers_at_value( pointer: Pos2, value: PlotPoint, + item: Option<(Id, usize)>, name: &str, plot: &PlotConfig<'_>, shapes: &mut Vec, @@ -2064,7 +2066,7 @@ pub(super) fn rulers_at_value( 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 let Some(custom_label) = label_formatter { - custom_label(name, &value) + custom_label(name, &value, item) } else if plot.show_x && plot.show_y { format!( "{}x = {:.*}\ny = {:.*}", diff --git a/egui_plot/src/items/values.rs b/egui_plot/src/items/values.rs index 51f66da..1142539 100644 --- a/egui_plot/src/items/values.rs +++ b/egui_plot/src/items/values.rs @@ -1,6 +1,6 @@ use std::ops::{Bound, RangeBounds, RangeInclusive}; -use egui::{lerp, Pos2, Shape, Stroke, Vec2}; +use egui::{lerp, Id, Pos2, Shape, Stroke, Vec2}; use crate::transform::PlotBounds; @@ -382,7 +382,7 @@ pub enum PlotGeometry<'a> { None, /// Point values (X-Y graphs) - Points(&'a [PlotPoint]), + Points(&'a [PlotPoint], Option), /// Rectangles (examples: boxes or bars) // Has currently no data, as it would require copying rects or iterating a list of pointers. diff --git a/egui_plot/src/lib.rs b/egui_plot/src/lib.rs index c8bf683..4ee297c 100644 --- a/egui_plot/src/lib.rs +++ b/egui_plot/src/lib.rs @@ -41,7 +41,7 @@ use axis::AxisWidget; use items::{horizontal_line, rulers_color, vertical_line}; use legend::LegendWidget; -type LabelFormatterFn<'a> = dyn Fn(&str, &PlotPoint) -> String + 'a; +type LabelFormatterFn<'a> = dyn Fn(&str, &PlotPoint, Option<(Id, usize)>) -> String + 'a; pub type LabelFormatter<'a> = Option>>; type GridSpacerFn<'a> = dyn Fn(GridInput) -> Vec + 'a; @@ -397,7 +397,7 @@ impl<'a> Plot<'a> { /// }).collect(); /// let line = Line::new(sin); /// Plot::new("my_plot").view_aspect(2.0) - /// .label_formatter(|name, value| { + /// .label_formatter(|name, value, _| { /// if !name.is_empty() { /// format!("{}: {:.*}%", name, 1, value.y) /// } else { @@ -409,7 +409,7 @@ impl<'a> Plot<'a> { /// ``` pub fn label_formatter( mut self, - label_formatter: impl Fn(&str, &PlotPoint) -> String + 'a, + label_formatter: impl Fn(&str, &PlotPoint, Option<(Id, usize)>) -> String + 'a, ) -> Self { self.label_formatter = Some(Box::new(label_formatter)); self @@ -1716,6 +1716,7 @@ impl<'a> PreparedPlot<'a> { items::rulers_at_value( pointer, value, + None, "", &plot, shapes, From dea35ec20bd7ae5bc31b331c8b0789dd22739fc4 Mon Sep 17 00:00:00 2001 From: Nicolas Date: Sat, 8 Feb 2025 01:04:25 +0100 Subject: [PATCH 2/2] fix tests --- demo/tests/snapshots/demos/Charts.png | 4 ++-- demo/tests/snapshots/demos/Custom Axes.png | 4 ++-- demo/tests/snapshots/demos/Interaction.png | 4 ++-- demo/tests/snapshots/demos/Items.png | 4 ++-- demo/tests/snapshots/demos/Legend.png | 4 ++-- demo/tests/snapshots/demos/Lines.png | 4 ++-- demo/tests/snapshots/demos/Linked Axes.png | 4 ++-- demo/tests/snapshots/demos/Markers.png | 4 ++-- demo/tests/snapshots/demos/Userdata.png | 3 +++ demo/tests/snapshots/light_mode.png | 4 ++-- demo/tests/snapshots/scale_0.50.png | 4 ++-- demo/tests/snapshots/scale_1.00.png | 4 ++-- demo/tests/snapshots/scale_1.39.png | 4 ++-- demo/tests/snapshots/scale_2.00.png | 4 ++-- 14 files changed, 29 insertions(+), 26 deletions(-) create mode 100644 demo/tests/snapshots/demos/Userdata.png diff --git a/demo/tests/snapshots/demos/Charts.png b/demo/tests/snapshots/demos/Charts.png index 625d7d7..da9d29a 100644 --- a/demo/tests/snapshots/demos/Charts.png +++ b/demo/tests/snapshots/demos/Charts.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7f2e6e4b9e9aec7c2f02faff3071c6d20c70b5122d02cf2549b3aa9d937b33f9 -size 81432 +oid sha256:725104ccade52b7988782044670b3d4bb025561f2cae856a5ccf46e85f0c1f79 +size 82495 diff --git a/demo/tests/snapshots/demos/Custom Axes.png b/demo/tests/snapshots/demos/Custom Axes.png index 26fa30d..79fdfe2 100644 --- a/demo/tests/snapshots/demos/Custom Axes.png +++ b/demo/tests/snapshots/demos/Custom Axes.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:491553ab1c2628d4f28af53d1b242db787e2c4a662aa44ed45ee17a190aae598 -size 69851 +oid sha256:f06b7b8192e1534b67225da34955386ea68a59161c03bcfcdf6c45f7e1f60be5 +size 70850 diff --git a/demo/tests/snapshots/demos/Interaction.png b/demo/tests/snapshots/demos/Interaction.png index 763cf1a..19dc35f 100644 --- a/demo/tests/snapshots/demos/Interaction.png +++ b/demo/tests/snapshots/demos/Interaction.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:d731cdff796638a19f9123e78be4bc0194bc29b74470b52fca8de65da189eb6b -size 74274 +oid sha256:817263cc213ae21d0d4106a187db98ee2dd04e22273bc7461005cbc9c16f61b5 +size 75337 diff --git a/demo/tests/snapshots/demos/Items.png b/demo/tests/snapshots/demos/Items.png index 5fd96f7..5c349a4 100644 --- a/demo/tests/snapshots/demos/Items.png +++ b/demo/tests/snapshots/demos/Items.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bdac172ced399b2ee7baf4ddd6cead4c629452440f8f47991eaf486851b153fd -size 103878 +oid sha256:a17b9bd0aa9176d70c2c7164c2dccc936242afd3063ea255c79f6d3f528814ed +size 104941 diff --git a/demo/tests/snapshots/demos/Legend.png b/demo/tests/snapshots/demos/Legend.png index 6f3b5be..9fcd22e 100644 --- a/demo/tests/snapshots/demos/Legend.png +++ b/demo/tests/snapshots/demos/Legend.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e2ea3e5de71aac86e3e96a46e8898f33c97658973bea821c353ff4f816fba13d -size 131204 +oid sha256:3f84339d9d82821662c540802da51466108d9700ac84a237dbc9e557552e3901 +size 132267 diff --git a/demo/tests/snapshots/demos/Lines.png b/demo/tests/snapshots/demos/Lines.png index 3376a19..33e426a 100644 --- a/demo/tests/snapshots/demos/Lines.png +++ b/demo/tests/snapshots/demos/Lines.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79a91cc56eac36bbf51c378eaa002d97dadb20ebd7d98ef1bcb81ffb338059d1 -size 114835 +oid sha256:20220602780ead0bbb94e81090a678169f51104563607a01f625e16f2612a892 +size 115898 diff --git a/demo/tests/snapshots/demos/Linked Axes.png b/demo/tests/snapshots/demos/Linked Axes.png index 5b80c3e..18e0791 100644 --- a/demo/tests/snapshots/demos/Linked Axes.png +++ b/demo/tests/snapshots/demos/Linked Axes.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2a9eca857d38b30c0ecc02727c5ff6c4b962f6648c25426d3a9d52040d195048 -size 81395 +oid sha256:402b26a2de1998a7b00a161a18d2ac5c49bca741efbc7929b27368f2d331e8bb +size 82458 diff --git a/demo/tests/snapshots/demos/Markers.png b/demo/tests/snapshots/demos/Markers.png index 4e30ca1..f849837 100644 --- a/demo/tests/snapshots/demos/Markers.png +++ b/demo/tests/snapshots/demos/Markers.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:29e0326610430e74ef775053df03edd1f114b3865070f5f6039a91389e6753fe -size 99436 +oid sha256:31a0ec44dd1a78a9df6b2117c2409540d83e0e31241f2c61e85697d0de5f6715 +size 100499 diff --git a/demo/tests/snapshots/demos/Userdata.png b/demo/tests/snapshots/demos/Userdata.png new file mode 100644 index 0000000..0cada8b --- /dev/null +++ b/demo/tests/snapshots/demos/Userdata.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3326918708e564956c523a8e93bc328b98e4b0a9ce6c43f413c45baa34eac610 +size 58891 diff --git a/demo/tests/snapshots/light_mode.png b/demo/tests/snapshots/light_mode.png index 4db2d18..45e3c60 100644 --- a/demo/tests/snapshots/light_mode.png +++ b/demo/tests/snapshots/light_mode.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c3c3102927c0e40bae43f06087029cf54cbcc454f50584444d71bf4f288ce46b -size 111838 +oid sha256:f067440bb6a3c07191340421073eb529d7890d4f07f48fefbefe8de059fbb3d9 +size 112946 diff --git a/demo/tests/snapshots/scale_0.50.png b/demo/tests/snapshots/scale_0.50.png index cc26637..1eff226 100644 --- a/demo/tests/snapshots/scale_0.50.png +++ b/demo/tests/snapshots/scale_0.50.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c78270b0cff295116a26bb8353b1d8eec745a6f1b2d59d5fa0282b90d40185f -size 58419 +oid sha256:568e3963403e16cec71199965007ec98a1d0d4a23d0394b4190d7c6b4dfaac73 +size 58842 diff --git a/demo/tests/snapshots/scale_1.00.png b/demo/tests/snapshots/scale_1.00.png index 3376a19..33e426a 100644 --- a/demo/tests/snapshots/scale_1.00.png +++ b/demo/tests/snapshots/scale_1.00.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:79a91cc56eac36bbf51c378eaa002d97dadb20ebd7d98ef1bcb81ffb338059d1 -size 114835 +oid sha256:20220602780ead0bbb94e81090a678169f51104563607a01f625e16f2612a892 +size 115898 diff --git a/demo/tests/snapshots/scale_1.39.png b/demo/tests/snapshots/scale_1.39.png index 7b1e51a..1f94563 100644 --- a/demo/tests/snapshots/scale_1.39.png +++ b/demo/tests/snapshots/scale_1.39.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a5ff6c81b29149f9de2306a9a8ec44424eec2af1d0ba6e94c559cf847fcb9fa6 -size 218520 +oid sha256:c7991fcf666085fec7503783dd6809ec0e77773658b73904eb021ec88c13e52d +size 220111 diff --git a/demo/tests/snapshots/scale_2.00.png b/demo/tests/snapshots/scale_2.00.png index db28f49..bbe3d11 100644 --- a/demo/tests/snapshots/scale_2.00.png +++ b/demo/tests/snapshots/scale_2.00.png @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:21f5f0ec6bc97698d1d36ce2ceba8d53c5be76cd03d4a8dbb25b7ae8c813196a -size 244000 +oid sha256:9e576f37723cce49c31027637aac0ec88f8ba1a52b89eb640e3b12fb306f8bb3 +size 246436