diff --git a/README.md b/README.md index b566d76..dce23a2 100644 --- a/README.md +++ b/README.md @@ -52,10 +52,10 @@ See below for an overview of the progress. - [ ] Plotting functionality - [x] Line plot - [x] Text plot - - [ ] Scatter plot - - [ ] Bar plot - - [ ] Vertical - - [ ] Horizontal + - [x] Scatter plot + - [x] Bar plot + - [x] Vertical + - [x] Horizontal - [ ] Error bar plot - [ ] Vertical - [ ] Horizontal diff --git a/implot-examples/examples/bar_plots.rs b/implot-examples/examples/bar_plots.rs new file mode 100644 index 0000000..550b8ed --- /dev/null +++ b/implot-examples/examples/bar_plots.rs @@ -0,0 +1,76 @@ +//! This example demonstrates how bar plots are to be used. For more general +//! features of the libray, see the line_plots example. + +use imgui::{im_str, CollapsingHeader, Condition, Ui, Window}; +use implot::{push_style_var_f32, push_style_var_u32, Marker, Plot, PlotBars, StyleVar}; + +mod support; + +fn show_basic_vertical_plot(ui: &Ui) { + ui.text(im_str!("This header shows a simple vertical bar plot.")); + let content_width = ui.window_content_region_width(); + Plot::new("Vertical bar 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(|| { + // 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]; + let values = vec![0.1, 0.2, 0.3, 0.4]; + PlotBars::new("legend label") + .with_bar_width(0.1) + .plot(&axis_positions, &values); + }); +} + +fn show_basic_horizontal_plot(ui: &Ui) { + ui.text(im_str!("This header shows a simple horizontal bar plot.")); + let content_width = ui.window_content_region_width(); + Plot::new("Horizontal bar 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(|| { + // 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]; + let values = vec![0.1, 0.2, 0.3, 0.4]; + PlotBars::new("legend label") + .with_bar_width(0.05) + .with_horizontal_bars() + .plot(&axis_positions, &values); + }); +} + +fn main() { + let system = support::init(file!()); + let mut showing_demo = false; + system.main_loop(move |_, ui| { + Window::new(im_str!("Line plots example")) + .size([430.0, 450.0], Condition::FirstUseEver) + .build(ui, || { + ui.text(im_str!("Hello from implot-rs!")); + ui.text_wrapped(im_str!( + "The headers here demo the bar plotting features of the library. \ + Have a look at the example source code to see how they are implemented.\n\ + Check out the demo from ImPlot itself first \ + (by enabling the 'Show demo' checkbox) for instructions \ + on how to interact with ImPlot plots." + )); + + ui.checkbox(im_str!("Show demo"), &mut showing_demo); + + // Show individual examples in collapsed headers + if CollapsingHeader::new(im_str!("Basic vertical plot")).build(&ui) { + show_basic_vertical_plot(&ui); + } + + if CollapsingHeader::new(im_str!("Basic horizontal plot")).build(&ui) { + show_basic_horizontal_plot(&ui); + } + }); + + if showing_demo { + implot::show_demo_window(&mut showing_demo); + } + }); +} diff --git a/implot-examples/examples/scatter_plot.rs b/implot-examples/examples/scatter_plots.rs similarity index 97% rename from implot-examples/examples/scatter_plot.rs rename to implot-examples/examples/scatter_plots.rs index bd64713..925e3c3 100644 --- a/implot-examples/examples/scatter_plot.rs +++ b/implot-examples/examples/scatter_plots.rs @@ -8,7 +8,7 @@ mod support; fn show_basic_plot(ui: &Ui) { ui.text(im_str!( - "This header just plots a line with as little code as possible." + "This header just draws a scatter plot with as little code as possible." )); let content_width = ui.window_content_region_width(); Plot::new("Simple scatter plot") diff --git a/src/lib.rs b/src/lib.rs index 0ce4a76..8c25a51 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -444,7 +444,7 @@ impl PlotLine { /// Struct to provide functionality for creating a scatter plot pub struct PlotScatter { - /// Label to show in the legend for this line + /// Label to show in the legend for this scatter plot label: String, } @@ -476,6 +476,82 @@ impl PlotScatter { } } +/// Struct to provide bar plotting functionality. +pub struct PlotBars { + /// Label to show in the legend for this line + label: String, + + /// Width of the bars, in plot coordinate terms + bar_width: f64, + + /// Horizontal bar mode + horizontal_bars: bool, +} + +impl PlotBars { + /// Create a new bar plot to be shown. Defaults to drawing vertical bars. + /// Does not draw anything yet. + pub fn new(label: &str) -> Self { + Self { + label: label.to_owned(), + bar_width: 0.67, // Default value taken from C++ implot + horizontal_bars: false, + } + } + + /// Set the width of the bars + pub fn with_bar_width(mut self, bar_width: f64) -> Self { + self.bar_width = bar_width; + self + } + + /// Set the bars to be horizontal (default is vertical) + pub fn with_horizontal_bars(mut self) -> Self { + self.horizontal_bars = true; + self + } + + /// 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 + /// bar is drawn, and the `bar_values` specify what values the bars have. + pub fn plot(&self, axis_positions: &Vec, bar_values: &Vec) { + let number_of_points = axis_positions.len().min(bar_values.len()); + // If there is no data to plot, we stop here + if number_of_points == 0 { + return; + } + unsafe { + // C++ implot has separate functions for the two variants, but the interfaces + // are the same, so they are unified here. The x and y values have different + // meanings though, hence the swapping around before they are passed to the + // plotting function. + let (plot_function, x, y); + if self.horizontal_bars { + plot_function = sys::ImPlot_PlotBarsHdoublePtrdoublePtr + as unsafe extern "C" fn(*const i8, *const f64, *const f64, i32, f64, i32, i32); + x = bar_values; + y = axis_positions; + } else { + plot_function = sys::ImPlot_PlotBarsdoublePtrdoublePtr + as unsafe extern "C" fn(*const i8, *const f64, *const f64, i32, f64, i32, i32); + x = axis_positions; + y = bar_values; + }; + + plot_function( + im_str!("{}", self.label).as_ptr() as *const i8, + x.as_ptr(), + y.as_ptr(), + number_of_points as i32, // "as" casts saturate as of Rust 1.45. This is safe here. + self.bar_width, + 0, // No offset + std::mem::size_of::() as i32, // Stride, set to one f64 for the standard use case + ); + } + } +} + /// Struct to provide functionality for adding text within a plot pub struct PlotText { /// Label to show in plot