Added better Y axis handling and API coverage

This commit is contained in:
4bb4 2020-10-18 16:22:37 +02:00
parent 44a065c8c7
commit 0af4203d4f
5 changed files with 248 additions and 138 deletions

View file

@ -1,6 +1,6 @@
[package] [package]
name = "implot" name = "implot"
version = "0.1.0" version = "0.2.0"
edition = "2018" edition = "2018"
authors = ["Sandro Merkli", "implot-rs contributors"] authors = ["Sandro Merkli", "implot-rs contributors"]
description = "Rust bindings to https://github.com/epezent/implot" description = "Rust bindings to https://github.com/epezent/implot"

View file

@ -6,6 +6,10 @@ Rust bindings for [ImPlot](https://github.com/epezent/implot), built by running
The bindings are currently based on ImPlot version 0.7. See the status section below for The bindings are currently based on ImPlot version 0.7. See the status section below for
detailed information on implementation status. detailed information on implementation status.
**Important note:** As long as the code is pre-1.0 release, the API is expected to have
breaking changes between minor versions. Patch versions should be backwards compatible.
After 1.0, semver will be followed more properly.
![demo](demo.png) ![demo](demo.png)
## Requirements ## Requirements
@ -20,27 +24,20 @@ clone the repo, change into the `implot-examples` directory and try for example
``` ```
## Documentation ## Documentation
Since the crate is not released yet, the documentation is not hosted yet either. You For released versions, see
can build it yourself however by cloning this repo and then doing [![Docs.rs documentation](https://docs.rs/implot/badge.svg)](https://docs.rs/implot/).
Make sure you are looking at the right release, since the API is still changing.
For the master branch, can build it yourself however by cloning this repo and then doing
``` ```
cargo doc --open cargo doc --open
``` ```
An effort is made to document everything as it is being added. Feel free to open an issue An effort is made to document everything as it is being added. Feel free to open an issue
if documentation is unclear or lacking. if documentation is unclear or lacking.
## Design approach
This repo tries to follow the approaches and style used in `imgui-rs` somewhat closely,
because implot is to be used within imgui programs, and hence keeping the interfaces
and design philosophies close should make it easier to do that.
If you spot any design inconsistencies or paper cuts, feel free to open an issue. ## Implementation status
Currently a work in progress, coverage of the C++ API is increased steadily. The author
## Status is open to collaboration, if you'd like to help, feel free to reach out via a Github issue.
Currently a work in progress. The author is open to collaboration, if you'd like to
help, feel free to reach out via a Github issue.
Note that the API is not stabilized yet and expected to change as development progresses.
Once there are actual releases on crates.io, semantic versioning will be followed.
At this point, raw bindings are working in implot-sys, and more idiomatic interfaces At this point, raw bindings are working in implot-sys, and more idiomatic interfaces
for plot creation as well a subset of the functionality for plots are implemented. for plot creation as well a subset of the functionality for plots are implemented.
@ -69,20 +66,30 @@ for plot creation as well a subset of the functionality for plots are implemente
- [x] Styling colors - [x] Styling colors
- [x] Styling variables - [x] Styling variables
- [x] Colormaps - [x] Colormaps
- [ ] Plot querying - [x] Plot querying
- [x] is hovered - [x] is hovered
- [x] mouse position in plot - [x] mouse position in plot
- [x] plot limits - [x] plot limits
- [x] is queried - [x] is queried
- [x] get plot query - [x] get plot query
- [ ] Choice of y axis - [x] are axes hovered
- [x] Choice of y axis
- [ ] Utils - [ ] Utils
- [x] Plot limit setting - [x] Plot limit setting
- [x] imgui-rs style safe push/pop stacks - [x] imgui-rs style safe push/pop stacks
- [x] Plot tick setting - [x] Plot tick setting
- [ ] Input remapping - [ ] Input remapping
- [ ] Set Y axis setting for subsequent elements - [x] Set Y axis setting for subsequent elements
- [ ] Set non-default Y axis ticks and labels
- [ ] Plot position and size reading - [ ] Plot position and size reading
- [ ] Pixel to plot position - [ ] Pixel to plot position
- [ ] Plot to pixel position - [ ] Plot to pixel position
- [ ] Push/pop plotclip rect (?) - [ ] Push/pop plotclip rect (?)
# Developer documentation
## Design approach
This repo tries to follow the approaches and style used in `imgui-rs` somewhat closely,
because implot is to be used within imgui programs, and hence keeping the interfaces
and design philosophies close should make it easier to do that.
If you spot any design inconsistencies or paper cuts, feel free to open an issue.

View file

@ -5,8 +5,9 @@ use imgui::{im_str, CollapsingHeader, Condition, Ui, Window};
use implot::{ use implot::{
get_plot_limits, get_plot_mouse_position, get_plot_query, is_plot_hovered, is_plot_queried, get_plot_limits, get_plot_mouse_position, get_plot_query, is_plot_hovered, is_plot_queried,
push_style_color, push_style_var_f32, push_style_var_i32, set_colormap_from_preset, push_style_color, push_style_var_f32, push_style_var_i32, set_colormap_from_preset,
set_colormap_from_vec, AxisFlags, Colormap, Context, ImPlotLimits, ImPlotPoint, ImPlotRange, set_colormap_from_vec, set_plot_y_axis, AxisFlags, Colormap, Context, ImPlotLimits,
ImVec4, Marker, Plot, PlotColorElement, PlotFlags, PlotLine, PlotUi, StyleVar, ImPlotPoint, ImPlotRange, ImVec4, Marker, Plot, PlotColorElement, PlotFlags, PlotLine, PlotUi,
StyleVar, YAxisChoice,
}; };
mod support; mod support;
@ -28,9 +29,43 @@ fn show_basic_plot(ui: &Ui, plot_ui: &PlotUi) {
}); });
} }
fn show_two_yaxis_plot(ui: &Ui, plot_ui: &PlotUi) {
ui.text(im_str!(
"This header shows how to create a plot with multiple Y axes."
));
let content_width = ui.window_content_region_width();
Plot::new("Multiple Y axis plots")
// 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)
.with_plot_flags(&(PlotFlags::NONE | PlotFlags::Y_AXIS_2))
.y_limits(
&ImPlotRange { Min: 0.0, Max: 1.0 },
YAxisChoice::First,
Condition::Always,
)
.y_limits(
&ImPlotRange { Min: 1.0, Max: 3.5 },
YAxisChoice::Second,
Condition::Always,
)
.build(plot_ui, || {
let x_positions = vec![0.1, 0.9];
// The first Y axis is the default
let y_positions = vec![0.1, 0.9];
PlotLine::new("legend label").plot(&x_positions, &y_positions);
// Now we switch to the second axis for the next call
set_plot_y_axis(YAxisChoice::Second);
let y_positions = vec![3.3, 1.2];
PlotLine::new("legend label two").plot(&x_positions, &y_positions);
});
}
fn show_configurable_plot(ui: &Ui, plot_ui: &PlotUi) { fn show_configurable_plot(ui: &Ui, plot_ui: &PlotUi) {
ui.text(im_str!( ui.text(im_str!(
"This header demos what we can configure about plots. €." "This header demos what we can configure about plots."
)); ));
// Settings for the plot // Settings for the plot
@ -79,14 +114,15 @@ fn show_configurable_plot(ui: &Ui, plot_ui: &PlotUi) {
Min: y_min, Min: y_min,
Max: y_max, Max: y_max,
}, },
YAxisChoice::First,
Condition::Always, Condition::Always,
) )
.x_ticks(&x_ticks, false) .x_ticks(&x_ticks, false)
.y_ticks_with_labels(&y_ticks, false) .y_ticks_with_labels(YAxisChoice::First, &y_ticks, false)
// If any of these flag setting calls are omitted, the defaults are used. // If any of these flag setting calls are omitted, the defaults are used.
.with_plot_flags(&plot_flags) .with_plot_flags(&plot_flags)
.with_x_axis_flags(&x_axis_flags) .with_x_axis_flags(&x_axis_flags)
.with_y_axis_flags(&y_axis_flags) .with_y_axis_flags(YAxisChoice::First, &y_axis_flags)
.build(plot_ui, || { .build(plot_ui, || {
PlotLine::new("A line").plot(&vec![2.1, 2.9], &vec![1.1, 1.9]); PlotLine::new("A line").plot(&vec![2.1, 2.9], &vec![1.1, 1.9]);
}); });
@ -107,17 +143,21 @@ fn show_query_features_plot(ui: &Ui, plot_ui: &PlotUi) {
Plot::new("Plot querying") Plot::new("Plot querying")
.size(content_width, 300.0) .size(content_width, 300.0)
.x_limits(&ImPlotRange { Min: 0.0, Max: 5.0 }, Condition::FirstUseEver) .x_limits(&ImPlotRange { Min: 0.0, Max: 5.0 }, Condition::FirstUseEver)
.y_limits(&ImPlotRange { Min: 0.0, Max: 5.0 }, Condition::FirstUseEver) .y_limits(
&ImPlotRange { Min: 0.0, Max: 5.0 },
YAxisChoice::First,
Condition::FirstUseEver,
)
.with_plot_flags(&(PlotFlags::NONE | PlotFlags::QUERY)) .with_plot_flags(&(PlotFlags::NONE | PlotFlags::QUERY))
.build(plot_ui, || { .build(plot_ui, || {
if is_plot_hovered() { if is_plot_hovered() {
hover_pos = Some(get_plot_mouse_position()); hover_pos = Some(get_plot_mouse_position(None));
} }
if is_plot_queried() { if is_plot_queried() {
query_limits = Some(get_plot_query()); query_limits = Some(get_plot_query(None));
} }
plot_limits = Some(get_plot_limits()); plot_limits = Some(get_plot_limits(None));
}); });
// Print some previously-exfiltrated info. This is because calling // Print some previously-exfiltrated info. This is because calling
@ -153,10 +193,11 @@ fn show_style_plot(ui: &Ui, plot_ui: &PlotUi) {
Min: -1.0, Min: -1.0,
Max: 3.0, Max: 3.0,
}, },
YAxisChoice::First,
Condition::Always, Condition::Always,
) )
.with_plot_flags(&(PlotFlags::NONE)) .with_plot_flags(&(PlotFlags::NONE))
.with_y_axis_flags(&(AxisFlags::NONE)) .with_y_axis_flags(YAxisChoice::First, &(AxisFlags::NONE))
.build(plot_ui, || { .build(plot_ui, || {
// Markers can be selected as shown here. The markers are internally represented // Markers can be selected as shown here. The markers are internally represented
// as an u32, hence this calling style. // as an u32, hence this calling style.
@ -268,6 +309,9 @@ fn main() {
if CollapsingHeader::new(im_str!("Colormap selection")).build(&ui) { if CollapsingHeader::new(im_str!("Colormap selection")).build(&ui) {
show_colormaps_plot(&ui, &plot_ui); show_colormaps_plot(&ui, &plot_ui);
} }
if CollapsingHeader::new(im_str!("Multiple Y Axes")).build(&ui) {
show_two_yaxis_plot(&ui, &plot_ui);
}
}); });
if showing_demo { if showing_demo {

View file

@ -28,6 +28,28 @@ mod context;
mod plot; mod plot;
mod plot_elements; mod plot_elements;
// The bindings for some reason don't contain this - it has to match the IMPLOT_AUTO from
// the original C++ header for things to work properly.
const IMPLOT_AUTO: i32 = -1;
// Number of Y axes, this is used in a bunch of places for storing things like settings.
// If this changes, also change the YAxisChoice enum.
const NUMBER_OF_Y_AXES: usize = 3;
/// Choice of Y axis. This an enum instead of just an integer so as to make it impossible
/// to select a Y axis that is not present - this makes it easier to avoid `Result`-type
/// return values on functions that could otherwise not really fail.
// Implementation note: This enum is converted straight to an usize index in a few places
// so we can store data about individual axes in arrays, so this pretty much should stay
// just a mapping of words to numbers.
#[derive(Clone)]
#[repr(i32)]
pub enum YAxisChoice {
First = 0,
Second = 1,
Third = 2,
}
/// A temporary reference for building plots. This does not really do anything on its own at /// A temporary reference for building plots. This does not really do anything on its own at
/// this point, but it is used to enforce that a context is created and active for other features, /// this point, but it is used to enforce that a context is created and active for other features,
/// such as creating plots. /// such as creating plots.
@ -251,9 +273,6 @@ pub fn push_style_color(
/// Tracks a change pushed to the style color stack /// Tracks a change pushed to the style color stack
pub struct StyleColorToken { pub struct StyleColorToken {
/// Whether this token has been popped or not. /// Whether this token has been popped or not.
/// TODO(4bb4) figure out if it is a good idea to warn about this not being popped when it is
/// dropped - this may not be a good idea since users may want to push some style vars for
/// longer durations.
was_popped: bool, was_popped: bool,
} }
@ -311,9 +330,6 @@ pub fn push_style_var_imvec2(element: &StyleVar, value: ImVec2) -> StyleVarToken
/// Tracks a change pushed to the style variable stack /// Tracks a change pushed to the style variable stack
pub struct StyleVarToken { pub struct StyleVarToken {
/// Whether this token has been popped or not. /// Whether this token has been popped or not.
/// TODO(4bb4) figure out if it is a good idea to warn about this not being popped when it is
/// dropped - this may not be a good idea since users may want to push some style vars for
/// longer durations.
was_popped: bool, was_popped: bool,
} }
@ -341,47 +357,79 @@ pub fn is_plot_queried() -> bool {
unsafe { sys::ImPlot_IsPlotQueried() } unsafe { sys::ImPlot_IsPlotQueried() }
} }
/// Returns the mouse position in x,y coordinates of the current or most recent plot. Currently /// Returns the mouse position in x,y coordinates of the current or most recent plot,
/// pertains to whatever Y axis was most recently selected. TODO(4bb4) add y axis selection /// for the specified choice of Y axis. If `None` is the Y axis choice, that means the
pub fn get_plot_mouse_position() -> ImPlotPoint { /// most recently selected Y axis is chosen.
let y_axis_selection = 0; pub fn get_plot_mouse_position(y_axis_choice: Option<YAxisChoice>) -> ImPlotPoint {
let y_axis_choice_i32 = match y_axis_choice {
Some(choice) => choice as i32,
None => IMPLOT_AUTO,
};
let mut point = ImPlotPoint { x: 0.0, y: 0.0 }; // doesn't seem to have default() let mut point = ImPlotPoint { x: 0.0, y: 0.0 }; // doesn't seem to have default()
unsafe { unsafe {
sys::ImPlot_GetPlotMousePos(&mut point as *mut ImPlotPoint, y_axis_selection); sys::ImPlot_GetPlotMousePos(&mut point as *mut ImPlotPoint, y_axis_choice_i32);
} }
point point
} }
/// Returns the current or most recent plot axis range. Currently pertains to whatever Y axis was /// Returns the current or most recent plot axis range for the specified choice of Y axis. If
/// most recently selected. TODO(4bb4) add y axis selection /// `None` is the Y axis choice, that means the most recently selected Y axis is chosen.
pub fn get_plot_limits() -> ImPlotLimits { pub fn get_plot_limits(y_axis_choice: Option<YAxisChoice>) -> ImPlotLimits {
let y_axis_selection = 0; let y_axis_choice_i32 = match y_axis_choice {
Some(choice) => choice as i32,
None => IMPLOT_AUTO,
};
// ImPlotLimits doesn't seem to have default() // ImPlotLimits doesn't seem to have default()
let mut limits = ImPlotLimits { let mut limits = ImPlotLimits {
X: ImPlotRange { Min: 0.0, Max: 0.0 }, X: ImPlotRange { Min: 0.0, Max: 0.0 },
Y: ImPlotRange { Min: 0.0, Max: 0.0 }, Y: ImPlotRange { Min: 0.0, Max: 0.0 },
}; };
unsafe { unsafe {
sys::ImPlot_GetPlotLimits(&mut limits as *mut ImPlotLimits, y_axis_selection); sys::ImPlot_GetPlotLimits(&mut limits as *mut ImPlotLimits, y_axis_choice_i32);
} }
limits limits
} }
/// Returns the query limits of the current or most recent plot. Currently pertains to whatever Y /// Returns the query limits of the current or most recent plot, for the specified choice of Y
/// axis was most recently selected. TODO(4bb4) add y axis selection /// axis. If `None` is the Y axis choice, that means the most recently selected Y axis is chosen.
pub fn get_plot_query() -> ImPlotLimits { pub fn get_plot_query(y_axis_choice: Option<YAxisChoice>) -> ImPlotLimits {
let y_axis_selection = 0; let y_axis_choice_i32 = match y_axis_choice {
Some(choice) => choice as i32,
None => IMPLOT_AUTO,
};
// ImPlotLimits doesn't seem to have default() // ImPlotLimits doesn't seem to have default()
let mut limits = ImPlotLimits { let mut limits = ImPlotLimits {
X: ImPlotRange { Min: 0.0, Max: 0.0 }, X: ImPlotRange { Min: 0.0, Max: 0.0 },
Y: ImPlotRange { Min: 0.0, Max: 0.0 }, Y: ImPlotRange { Min: 0.0, Max: 0.0 },
}; };
unsafe { unsafe {
sys::ImPlot_GetPlotQuery(&mut limits as *mut ImPlotLimits, y_axis_selection); sys::ImPlot_GetPlotQuery(&mut limits as *mut ImPlotLimits, y_axis_choice_i32);
} }
limits limits
} }
/// Set the Y axis to be used for any upcoming plot elements
pub fn set_plot_y_axis(y_axis_choice: YAxisChoice) {
unsafe {
sys::ImPlot_SetPlotYAxis(y_axis_choice as i32);
}
}
/// Returns true if the XAxis plot area in the current plot is hovered.
pub fn is_plot_x_axis_hovered() -> bool {
unsafe { sys::ImPlot_IsPlotXAxisHovered() }
}
/// Returns true if the YAxis[n] plot area in the current plot is hovered. If `None` is the Y axis
/// choice, that means the most recently selected Y axis is chosen.
pub fn is_plot_y_axis_hovered(y_axis_choice: Option<YAxisChoice>) -> bool {
let y_axis_choice_i32 = match y_axis_choice {
Some(choice) => choice as i32,
None => IMPLOT_AUTO,
};
unsafe { sys::ImPlot_IsPlotYAxisHovered(y_axis_choice_i32) }
}
// --- Demo window ------------------------------------------------------------------------------- // --- Demo window -------------------------------------------------------------------------------
/// Show the demo window for poking around what functionality implot has to /// 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 /// offer. Note that not all of this is necessarily implemented in implot-rs

View file

@ -8,7 +8,7 @@ pub use sys::imgui::Condition;
use sys::imgui::{im_str, ImString}; use sys::imgui::{im_str, ImString};
pub use sys::{ImPlotLimits, ImPlotPoint, ImPlotRange, ImVec2, ImVec4}; pub use sys::{ImPlotLimits, ImPlotPoint, ImPlotRange, ImVec2, ImVec4};
use crate::{Context, PlotUi}; use crate::{Context, PlotUi, YAxisChoice, NUMBER_OF_Y_AXES};
const DEFAULT_PLOT_SIZE_X: f32 = 400.0; const DEFAULT_PLOT_SIZE_X: f32 = 400.0;
const DEFAULT_PLOT_SIZE_Y: f32 = 400.0; const DEFAULT_PLOT_SIZE_Y: f32 = 400.0;
@ -107,11 +107,11 @@ pub struct Plot {
/// X axis limits, if present /// X axis limits, if present
x_limits: Option<ImPlotRange>, x_limits: Option<ImPlotRange>,
/// Y axis limits, if present /// Y axis limits, if present
y_limits: Option<ImPlotRange>, y_limits: [Option<ImPlotRange>; NUMBER_OF_Y_AXES],
/// Condition on which the x limits are set /// Condition on which the x limits are set
x_limit_condition: Option<Condition>, x_limit_condition: Option<Condition>,
/// Condition on which the y limits are set (first y axis for now) /// Condition on which the y limits are set for each of the axes
y_limit_condition: Option<Condition>, y_limit_condition: [Option<Condition>; NUMBER_OF_Y_AXES],
/// Positions for custom X axis ticks, if any /// Positions for custom X axis ticks, if any
x_tick_positions: Option<Vec<f64>>, x_tick_positions: Option<Vec<f64>>,
/// Labels for custom X axis ticks, if any. I'd prefer to store these together /// Labels for custom X axis ticks, if any. I'd prefer to store these together
@ -124,33 +124,31 @@ pub struct Plot {
/// Whether to also show the default X ticks when showing custom ticks or not /// Whether to also show the default X ticks when showing custom ticks or not
show_x_default_ticks: bool, show_x_default_ticks: bool,
/// Positions for custom Y axis ticks, if any /// Positions for custom Y axis ticks, if any
y_tick_positions: Option<Vec<f64>>, y_tick_positions: [Option<Vec<f64>>; NUMBER_OF_Y_AXES],
/// Labels for custom Y axis ticks, if any. I'd prefer to store these together /// Labels for custom Y axis ticks, if any. I'd prefer to store these together
/// with the positions in one vector of an algebraic data type, but this would mean extra /// with the positions in one vector of an algebraic data type, but this would mean extra
/// copies when it comes time to draw the plot because the C++ library expects separate lists. /// copies when it comes time to draw the plot because the C++ library expects separate lists.
/// The data is stored as ImStrings because those are null-terminated, and since we have to /// The data is stored as ImStrings because those are null-terminated, and since we have to
/// convert to null-terminated data anyway, we may as well do that directly instead of cloning /// convert to null-terminated data anyway, we may as well do that directly instead of cloning
/// Strings and converting them afterwards. /// Strings and converting them afterwards.
y_tick_labels: Option<Vec<ImString>>, y_tick_labels: [Option<Vec<ImString>>; NUMBER_OF_Y_AXES],
/// Whether to also show the default Y ticks when showing custom ticks or not /// Whether to also show the default Y ticks when showing custom ticks or not
show_y_default_ticks: bool, show_y_default_ticks: [bool; NUMBER_OF_Y_AXES],
/// Flags relating to the plot TODO(4bb4) make those into bitflags /// Flags relating to the plot TODO(4bb4) make those into bitflags
plot_flags: sys::ImPlotFlags, plot_flags: sys::ImPlotFlags,
/// Flags relating to the first x axis of the plot TODO(4bb4) make those into bitflags /// Flags relating to the X axis of the plot TODO(4bb4) make those into bitflags
x_flags: sys::ImPlotAxisFlags, x_flags: sys::ImPlotAxisFlags,
/// Flags relating to the first y axis of the plot TODO(4bb4) make those into bitflags /// Flags relating to the each of the Y axes of the plot TODO(4bb4) make those into bitflags
y_flags: sys::ImPlotAxisFlags, y_flags: [sys::ImPlotAxisFlags; NUMBER_OF_Y_AXES],
/// Flags relating to the second y axis of the plot (if present, otherwise ignored)
/// TODO(4bb4) make those into bitflags
y2_flags: sys::ImPlotAxisFlags,
/// Flags relating to the third y axis of the plot (if present, otherwise ignored)
/// TODO(4bb4) make those into bitflags
y3_flags: sys::ImPlotAxisFlags,
} }
impl Plot { impl Plot {
/// Create a new plot with some defaults set. Does not draw anything yet. /// Create a new plot with some defaults set. Does not draw anything yet.
pub fn new(title: &str) -> Self { pub fn new(title: &str) -> Self {
// Needed for initialization, see https://github.com/rust-lang/rust/issues/49147
const POS_NONE: Option<Vec<f64>> = None;
const TICK_NONE: Option<Vec<ImString>> = None;
// TODO(4bb4) question these defaults, maybe remove some of them // TODO(4bb4) question these defaults, maybe remove some of them
Self { Self {
title: im_str!("{}", title), title: im_str!("{}", title),
@ -159,20 +157,18 @@ impl Plot {
x_label: im_str!("").into(), x_label: im_str!("").into(),
y_label: im_str!("").into(), y_label: im_str!("").into(),
x_limits: None, x_limits: None,
y_limits: None, y_limits: [None; NUMBER_OF_Y_AXES],
x_limit_condition: None, x_limit_condition: None,
y_limit_condition: None, y_limit_condition: [None; NUMBER_OF_Y_AXES],
x_tick_positions: None, x_tick_positions: None,
x_tick_labels: None, x_tick_labels: None,
show_x_default_ticks: false, show_x_default_ticks: false,
y_tick_positions: None, y_tick_positions: [POS_NONE; NUMBER_OF_Y_AXES],
y_tick_labels: None, y_tick_labels: [TICK_NONE; NUMBER_OF_Y_AXES],
show_y_default_ticks: false, show_y_default_ticks: [false; NUMBER_OF_Y_AXES],
plot_flags: PlotFlags::NONE.bits() as sys::ImPlotFlags, plot_flags: PlotFlags::NONE.bits() as sys::ImPlotFlags,
x_flags: AxisFlags::NONE.bits() as sys::ImPlotAxisFlags, x_flags: AxisFlags::NONE.bits() as sys::ImPlotAxisFlags,
y_flags: AxisFlags::NONE.bits() as sys::ImPlotAxisFlags, y_flags: [AxisFlags::NONE.bits() as sys::ImPlotAxisFlags; NUMBER_OF_Y_AXES],
y2_flags: AxisFlags::NONE.bits() as sys::ImPlotAxisFlags,
y3_flags: AxisFlags::NONE.bits() as sys::ImPlotAxisFlags,
} }
} }
@ -207,11 +203,18 @@ impl Plot {
self self
} }
/// Set the y limits of the plot /// Set the Y limits of the plot for the given Y axis. Call multiple times
/// to set for multiple axes.
#[inline] #[inline]
pub fn y_limits(mut self, limits: &ImPlotRange, condition: Condition) -> Self { pub fn y_limits(
self.y_limits = Some(*limits); mut self,
self.y_limit_condition = Some(condition); limits: &ImPlotRange,
y_axis_choice: YAxisChoice,
condition: Condition,
) -> Self {
let axis_index = y_axis_choice as usize;
self.y_limits[axis_index] = Some(*limits);
self.y_limit_condition[axis_index] = Some(condition);
self self
} }
@ -229,9 +232,15 @@ impl Plot {
/// the form of a tuple `(label_position, label_string)`. The `show_default` setting /// the form of a tuple `(label_position, label_string)`. The `show_default` setting
/// determines whether the default ticks are also shown. /// determines whether the default ticks are also shown.
#[inline] #[inline]
pub fn y_ticks(mut self, ticks: &Vec<f64>, show_default: bool) -> Self { pub fn y_ticks(
self.y_tick_positions = Some(ticks.clone()); mut self,
self.show_y_default_ticks = show_default; y_axis_choice: YAxisChoice,
ticks: &Vec<f64>,
show_default: bool,
) -> Self {
let axis_index = y_axis_choice as usize;
self.y_tick_positions[axis_index] = Some(ticks.clone());
self.show_y_default_ticks[axis_index] = show_default;
self self
} }
@ -256,12 +265,15 @@ impl Plot {
#[inline] #[inline]
pub fn y_ticks_with_labels( pub fn y_ticks_with_labels(
mut self, mut self,
y_axis_choice: YAxisChoice,
tick_labels: &Vec<(f64, String)>, tick_labels: &Vec<(f64, String)>,
show_default: bool, show_default: bool,
) -> Self { ) -> Self {
self.y_tick_positions = Some(tick_labels.iter().map(|x| x.0).collect()); let axis_index = y_axis_choice as usize;
self.y_tick_labels = Some(tick_labels.iter().map(|x| im_str!("{}", x.1)).collect()); self.y_tick_positions[axis_index] = Some(tick_labels.iter().map(|x| x.0).collect());
self.show_y_default_ticks = show_default; self.y_tick_labels[axis_index] =
Some(tick_labels.iter().map(|x| im_str!("{}", x.1)).collect());
self.show_y_default_ticks[axis_index] = show_default;
self self
} }
@ -279,24 +291,11 @@ impl Plot {
self self
} }
/// Set the axis flags for the first Y axis in this plot /// Set the axis flags for the selected Y axis in this plot
#[inline] #[inline]
pub fn with_y_axis_flags(mut self, flags: &AxisFlags) -> Self { pub fn with_y_axis_flags(mut self, y_axis_choice: YAxisChoice, flags: &AxisFlags) -> Self {
self.y_flags = flags.bits() as sys::ImPlotAxisFlags; let axis_index = y_axis_choice as usize;
self self.y_flags[axis_index] = flags.bits() as sys::ImPlotAxisFlags;
}
/// Set the axis flags for the second Y axis in this plot
#[inline]
pub fn with_y2_axis_flags(mut self, flags: &AxisFlags) -> Self {
self.y2_flags = flags.bits() as sys::ImPlotAxisFlags;
self
}
/// Set the axis flags for the third Y axis in this plot
#[inline]
pub fn with_y3_axis_flags(mut self, flags: &AxisFlags) -> Self {
self.y3_flags = flags.bits() as sys::ImPlotAxisFlags;
self self
} }
@ -309,19 +308,23 @@ impl Plot {
} }
} }
// Set X limits if specified // Set Y limits if specified
if let (Some(limits), Some(condition)) = (self.y_limits, self.y_limit_condition) { self.y_limits
// TODO(4bb4) allow for specification of multiple y limits, not just the first .iter()
let selected_y_axis = 0; .zip(self.y_limit_condition.iter())
.enumerate()
.for_each(|(k, (limits, condition))| {
if let (Some(limits), Some(condition)) = (limits, condition) {
unsafe { unsafe {
sys::ImPlot_SetNextPlotLimitsY( sys::ImPlot_SetNextPlotLimitsY(
limits.Min, limits.Min,
limits.Max, limits.Max,
condition as sys::ImGuiCond, *condition as sys::ImGuiCond,
selected_y_axis, k as i32,
); );
} }
} }
});
} }
/// Internal helper function to set tick labels in case they are specified. This does the /// Internal helper function to set tick labels in case they are specified. This does the
@ -351,9 +354,16 @@ impl Plot {
} }
} }
if self.y_tick_positions.is_some() && self.y_tick_positions.as_ref().unwrap().len() > 0 { self.y_tick_positions
let mut pointer_vec; // The vector of pointers we create has to have a longer lifetime .iter()
let labels_pointer = if let Some(labels_value) = &self.y_tick_labels { .zip(self.y_tick_labels.iter())
.zip(self.show_y_default_ticks.iter())
.enumerate()
.for_each(|(k, ((positions, labels), show_defaults))| {
if positions.is_some() && positions.as_ref().unwrap().len() > 0 {
// The vector of pointers we create has to have a longer lifetime
let mut pointer_vec;
let labels_pointer = if let Some(labels_value) = &labels {
pointer_vec = labels_value pointer_vec = labels_value
.iter() .iter()
.map(|x| x.as_ptr() as *const i8) .map(|x| x.as_ptr() as *const i8)
@ -365,14 +375,15 @@ impl Plot {
unsafe { unsafe {
sys::ImPlot_SetNextPlotTicksYdoublePtr( sys::ImPlot_SetNextPlotTicksYdoublePtr(
self.y_tick_positions.as_ref().unwrap().as_ptr(), positions.as_ref().unwrap().as_ptr(),
self.y_tick_positions.as_ref().unwrap().len() as i32, positions.as_ref().unwrap().len() as i32,
labels_pointer, labels_pointer,
self.show_y_default_ticks, *show_defaults,
0, // y axis selection, TODO(4bb4) make this configurable k as i32,
) )
} }
} }
});
} }
/// Attempt to show the plot. If this returns a token, the plot will actually /// Attempt to show the plot. If this returns a token, the plot will actually
@ -397,9 +408,9 @@ impl Plot {
}, },
self.plot_flags, self.plot_flags,
self.x_flags, self.x_flags,
self.y_flags, self.y_flags[0],
self.y2_flags, self.y_flags[1],
self.y3_flags, self.y_flags[2],
) )
}; };