From ff96a07ef3ce1f16d64428f67120d63d8ec62ec6 Mon Sep 17 00:00:00 2001 From: 4bb4 <67376761+4bb4@users.noreply.github.com> Date: Sun, 13 Sep 2020 15:06:59 +0200 Subject: [PATCH] Added scatter plot support --- implot-examples/examples/line_plots.rs | 3 +- implot-examples/examples/scatter_plot.rs | 88 ++++++++++++++++++++++++ implot-examples/examples/text_plots.rs | 3 +- src/lib.rs | 34 +++++++++ 4 files changed, 124 insertions(+), 4 deletions(-) create mode 100644 implot-examples/examples/scatter_plot.rs diff --git a/implot-examples/examples/line_plots.rs b/implot-examples/examples/line_plots.rs index 1932280..ef27a19 100644 --- a/implot-examples/examples/line_plots.rs +++ b/implot-examples/examples/line_plots.rs @@ -179,8 +179,7 @@ fn main() { ui.text(im_str!("Hello from implot-rs!")); ui.text_wrapped(im_str!( "The headers here demo the line plotting features of the library. \ - Have a look at the example source code to \ - see how they are implemented.\n\ + 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." diff --git a/implot-examples/examples/scatter_plot.rs b/implot-examples/examples/scatter_plot.rs new file mode 100644 index 0000000..bd64713 --- /dev/null +++ b/implot-examples/examples/scatter_plot.rs @@ -0,0 +1,88 @@ +//! This example demonstrates how scatter 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, PlotScatter, StyleVar}; + +mod support; + +fn show_basic_plot(ui: &Ui) { + ui.text(im_str!( + "This header just plots a line with as little code as possible." + )); + let content_width = ui.window_content_region_width(); + Plot::new("Simple scatter 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 x_positions = vec![0.1, 0.2, 0.1, 0.5, 0.9]; + let y_positions = vec![0.1, 0.1, 0.3, 0.3, 0.9]; + PlotScatter::new("legend label").plot(&x_positions, &y_positions); + }); +} + +fn show_custom_markers_plot(ui: &Ui) { + ui.text(im_str!( + "This header just plots a line with as little code as possible." + )); + let content_width = ui.window_content_region_width(); + Plot::new("Simple scatter 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(|| { + // Change to cross marker for one scatter plot call + let x_positions = vec![0.1, 0.2, 0.1, 0.5, 0.9]; + let y_positions = vec![0.1, 0.1, 0.3, 0.3, 0.9]; + let markerchoice = push_style_var_u32(&StyleVar::Marker, Marker::CROSS.bits()); + PlotScatter::new("legend label 1").plot(&x_positions, &y_positions); + markerchoice.pop(); + + // One can combine things like marker size and markor choice + let x_positions = vec![0.4, 0.1]; + let y_positions = vec![0.5, 0.3]; + let marker_choice = push_style_var_u32(&StyleVar::Marker, Marker::DIAMOND.bits()); + let marker_size = push_style_var_f32(&StyleVar::MarkerSize, 12.0); + PlotScatter::new("legend label 2").plot(&x_positions, &y_positions); + + // TODO(4bb4) check if these have to be in reverse push order + marker_size.pop(); + marker_choice.pop(); + }); +} + +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 scatter 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 scatter plot")).build(&ui) { + show_basic_plot(&ui); + } + + if CollapsingHeader::new(im_str!("Custom markers")).build(&ui) { + show_custom_markers_plot(&ui); + } + }); + + if showing_demo { + implot::show_demo_window(&mut showing_demo); + } + }); +} diff --git a/implot-examples/examples/text_plots.rs b/implot-examples/examples/text_plots.rs index 2a3f928..cf74565 100644 --- a/implot-examples/examples/text_plots.rs +++ b/implot-examples/examples/text_plots.rs @@ -40,8 +40,7 @@ fn main() { ui.text(im_str!("Hello from implot-rs!")); ui.text_wrapped(im_str!( "The headers here demo the text plotting features of the library. \ - Have a look at the example source code to see how they are \ - implemented.\n\ + 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." diff --git a/src/lib.rs b/src/lib.rs index 398cd99..0ce4a76 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -442,6 +442,40 @@ impl PlotLine { } } +/// Struct to provide functionality for creating a scatter plot +pub struct PlotScatter { + /// Label to show in the legend for this line + label: String, +} + +impl PlotScatter { + /// Create a new scatter plot to be shown. Does not draw anything yet. + pub fn new(label: &str) -> Self { + Self { + label: label.to_owned(), + } + } + + /// Draw a previously-created scatter plot. Use this in closures passed to + /// [`Plot::build()`](struct.Plot.html#method.build) + pub fn plot(&self, x: &Vec, y: &Vec) { + // If there is no data to plot, we stop here + if x.len().min(y.len()) == 0 { + return; + } + unsafe { + sys::ImPlot_PlotScatterdoublePtrdoublePtr( + im_str!("{}", self.label).as_ptr() as *const i8, + x.as_ptr(), + y.as_ptr(), + x.len().min(y.len()) as i32, // "as" casts saturate as of Rust 1.45. This is safe here. + 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