From 21aa28591c0f8d41bda84b56b89eeb1c8417fd24 Mon Sep 17 00:00:00 2001 From: 4bb4 <67376761+4bb4@users.noreply.github.com> Date: Sun, 31 Jan 2021 12:40:13 +0100 Subject: [PATCH] Added stem plots and legend entry hover checking --- .gitignore | 5 +- README.md | 4 +- implot-examples/examples-shared/src/lib.rs | 5 ++ .../examples-shared/src/line_plots.rs | 25 +++++++--- .../examples-shared/src/stem_plots.rs | 28 +++++++++++ src/lib.rs | 6 +++ src/plot_elements.rs | 50 ++++++++++++++++++- 7 files changed, 113 insertions(+), 10 deletions(-) create mode 100644 implot-examples/examples-shared/src/stem_plots.rs diff --git a/.gitignore b/.gitignore index 088ba6b..f79f3c2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,9 @@ # Generated by Cargo # will have compiled files and executables -/target/ +**/target/ + +# Editor temporary files +**/*.swp # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html diff --git a/README.md b/README.md index 1ccc593..6b904ed 100644 --- a/README.md +++ b/README.md @@ -83,7 +83,7 @@ created for 64-bit floats. - [x] Styling variables - [x] Colormaps - [x] Legend locations -- [ ] Plot querying +- [x] Plot querying - [x] is hovered - [x] mouse position in plot - [x] plot limits @@ -91,7 +91,7 @@ created for 64-bit floats. - [x] get plot query - [x] are axes hovered - [x] Choice of y axis - - [ ] Are legend entries hovered + - [x] Are legend entries hovered - [ ] Utils - [x] Plot limit setting - [x] imgui-rs style safe push/pop stacks diff --git a/implot-examples/examples-shared/src/lib.rs b/implot-examples/examples-shared/src/lib.rs index 4b55a9f..690ccbf 100644 --- a/implot-examples/examples-shared/src/lib.rs +++ b/implot-examples/examples-shared/src/lib.rs @@ -3,6 +3,7 @@ pub mod heatmaps; pub mod line_plots; pub mod scatter_plots; pub mod stairs_plots; +mod stem_plots; pub mod text_plots; use imgui::{im_str, Condition, Ui, Window}; @@ -43,5 +44,9 @@ pub fn show_demos(ui: &Ui, plot_ui: &PlotUi) { ui.separator(); ui.text(im_str!("Heatmaps:")); heatmaps::show_demo_headers(ui, plot_ui); + + ui.separator(); + ui.text(im_str!("Stem plots:")); + stem_plots::show_demo_headers(ui, plot_ui); }); } diff --git a/implot-examples/examples-shared/src/line_plots.rs b/implot-examples/examples-shared/src/line_plots.rs index f00b419..be744f4 100644 --- a/implot-examples/examples-shared/src/line_plots.rs +++ b/implot-examples/examples-shared/src/line_plots.rs @@ -3,12 +3,12 @@ use imgui::{im_str, CollapsingHeader, Condition, Ui}; use implot::{ - get_plot_limits, get_plot_mouse_position, get_plot_query, is_plot_hovered, is_plot_queried, - pixels_to_plot_vec2, plot_to_pixels_vec2, push_style_color, push_style_var_f32, - push_style_var_i32, set_colormap_from_preset, set_colormap_from_vec, set_plot_y_axis, - AxisFlags, Colormap, ImPlotLimits, ImPlotPoint, ImPlotRange, ImVec2, ImVec4, Marker, Plot, - PlotColorElement, PlotFlags, PlotLine, PlotLocation, PlotOrientation, PlotUi, StyleVar, - YAxisChoice, + get_plot_limits, get_plot_mouse_position, get_plot_query, is_legend_entry_hovered, + is_plot_hovered, is_plot_queried, pixels_to_plot_vec2, plot_to_pixels_vec2, push_style_color, + push_style_var_f32, push_style_var_i32, set_colormap_from_preset, set_colormap_from_vec, + set_plot_y_axis, AxisFlags, Colormap, ImPlotLimits, ImPlotPoint, ImPlotRange, ImVec2, ImVec4, + Marker, Plot, PlotColorElement, PlotFlags, PlotLine, PlotLocation, PlotOrientation, PlotUi, + StyleVar, YAxisChoice, }; pub fn show_basic_plot(ui: &Ui, plot_ui: &PlotUi) { @@ -156,6 +156,8 @@ pub fn show_query_features_plot(ui: &Ui, plot_ui: &PlotUi) { let mut hover_pos_from_pixels: Option = None; let mut plot_limits: Option = None; let mut query_limits: Option = None; + let mut legend1_hovered = false; + let mut legend2_hovered = false; // Draw a plot Plot::new("Plot querying") @@ -183,6 +185,12 @@ pub fn show_query_features_plot(ui: &Ui, plot_ui: &PlotUi) { None, )); + // Plot a line so we have a legend entry + PlotLine::new("Legend1").plot(&vec![2.0, 2.0], &vec![2.0, 1.0]); + PlotLine::new("Legend2").plot(&vec![0.0, 0.0], &vec![1.0, 1.0]); + legend1_hovered = is_legend_entry_hovered("Legend1"); + legend2_hovered = is_legend_entry_hovered("Legend2"); + if is_plot_queried() { query_limits = Some(get_plot_query(None)); } @@ -214,6 +222,11 @@ pub fn show_query_features_plot(ui: &Ui, plot_ui: &PlotUi) { if let Some(query) = query_limits { ui.text(im_str!("Query limits are {:#?}", query)); } + ui.text(im_str!( + "Legend hovering - 1: {}, 2: {}", + legend1_hovered, + legend2_hovered + )); // Try out converting pixel position to plot position if let Some(pos) = hover_pos_from_pixels { diff --git a/implot-examples/examples-shared/src/stem_plots.rs b/implot-examples/examples-shared/src/stem_plots.rs new file mode 100644 index 0000000..dc99c64 --- /dev/null +++ b/implot-examples/examples-shared/src/stem_plots.rs @@ -0,0 +1,28 @@ +//! This example demonstrates how stem plots are to be used. For more general +//! features of the libray, see the line_plots example. + +use imgui::{im_str, CollapsingHeader, Ui}; +use implot::{Plot, PlotStems, PlotUi}; + +pub fn show_basic_plot(ui: &Ui, plot_ui: &PlotUi) { + ui.text(im_str!("This header shows a simple stem plot.")); + let content_width = ui.window_content_region_width(); + Plot::new("Stem plot") + // The size call could also be omitted, though the defaults don't consider window + // width, which is why we're not doing so here. + .size(content_width, 300.0) + .build(plot_ui, || { + // If this is called outside a plot build callback, the program will panic. + let axis_positions = vec![0.2, 0.4, 0.6, 0.8, 0.9, 0.93]; + let values = vec![0.1, 0.2, 0.3, 0.4, 0.3, 0.8]; + PlotStems::new("legend label") + .with_reference_y(0.1) + .plot(&axis_positions, &values); + }); +} + +pub fn show_demo_headers(ui: &Ui, plot_ui: &PlotUi) { + if CollapsingHeader::new(im_str!("Stem plots")).build(&ui) { + show_basic_plot(&ui, &plot_ui); + } +} diff --git a/src/lib.rs b/src/lib.rs index 1979557..b388c0b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,6 +20,7 @@ use implot_sys as sys; // TODO(4bb4) facade-wrap these? pub use self::{context::*, plot::*, plot_elements::*}; +use imgui::im_str; pub use sys::{ImPlotLimits, ImPlotPoint, ImPlotRange, ImVec2, ImVec4}; mod context; @@ -572,6 +573,11 @@ pub fn is_plot_y_axis_hovered(y_axis_choice: Option) -> bool { unsafe { sys::ImPlot_IsPlotYAxisHovered(y_axis_choice_i32) } } +/// Returns true if the given item in the legend of the current plot is hovered. +pub fn is_legend_entry_hovered(legend_entry: &str) -> bool { + unsafe { sys::ImPlot_IsLegendEntryHovered(im_str!("{}", legend_entry).as_ptr() as *const i8) } +} + // --- Demo window ------------------------------------------------------------------------------- /// Show the demo window for poking around what functionality implot has to /// offer. Note that not all of this is necessarily implemented in implot-rs diff --git a/src/plot_elements.rs b/src/plot_elements.rs index 80b26ce..4742ee3 100644 --- a/src/plot_elements.rs +++ b/src/plot_elements.rs @@ -147,7 +147,7 @@ impl PlotBars { /// Draw a previously-created bar plot. Use this in closures passed to /// [`Plot::build()`](struct.Plot.html#method.build). The `axis_positions` - /// specify where on the corersponding axis (X for vertical mode, Y for horizontal mode) the + /// specify where on the corresponding axis (X for vertical mode, Y for horizontal mode) the /// bar is drawn, and the `bar_values` specify what values the bars have. pub fn plot(&self, axis_positions: &[f64], bar_values: &[f64]) { let number_of_points = axis_positions.len().min(bar_values.len()); @@ -332,3 +332,51 @@ impl PlotHeatmap { } } } + +/// Struct to provide stem plotting functionality. +pub struct PlotStems { + /// Label to show in the legend for this line + label: String, + + /// Reference value for the y value, which the stems are "with respect to" + reference_y: f64, +} + +impl PlotStems { + /// Create a new stem plot to be shown. Does not draw anything by itself, call + /// [`PlotStems::plot`] on the struct for that. + pub fn new(label: &str) -> Self { + Self { + label: label.to_owned(), + reference_y: 0.0, // Default value taken from C++ implot + } + } + + /// Set the reference y value for the stems + pub fn with_reference_y(mut self, reference_y: f64) -> Self { + self.reference_y = reference_y; + self + } + + /// Draw a previously-created stem plot. Use this in closures passed to + /// [`Plot::build()`](struct.Plot.html#method.build). The `axis_positions` specify where on the + /// X axis the stems are drawn, and the `stem_values` specify what values the stems have. + pub fn plot(&self, axis_positions: &[f64], stem_values: &[f64]) { + let number_of_points = axis_positions.len().min(stem_values.len()); + // If there is no data to plot, we stop here + if number_of_points == 0 { + return; + } + unsafe { + sys::ImPlot_PlotStemsdoublePtrdoublePtr( + im_str!("{}", self.label).as_ptr() as *const i8, + axis_positions.as_ptr(), + stem_values.as_ptr(), + number_of_points as i32, // "as" casts saturate as of Rust 1.45. This is safe here. + self.reference_y, + 0, // No offset + std::mem::size_of::() as i32, // Stride, set to one f64 for the standard use case + ); + } + } +}