Added better Y axis handling and API coverage
This commit is contained in:
parent
44a065c8c7
commit
0af4203d4f
5 changed files with 248 additions and 138 deletions
|
@ -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"
|
||||||
|
|
41
README.md
41
README.md
|
@ -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.
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
90
src/lib.rs
90
src/lib.rs
|
@ -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
|
||||||
|
|
187
src/plot.rs
187
src/plot.rs
|
@ -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())
|
||||||
unsafe {
|
.enumerate()
|
||||||
sys::ImPlot_SetNextPlotLimitsY(
|
.for_each(|(k, (limits, condition))| {
|
||||||
limits.Min,
|
if let (Some(limits), Some(condition)) = (limits, condition) {
|
||||||
limits.Max,
|
unsafe {
|
||||||
condition as sys::ImGuiCond,
|
sys::ImPlot_SetNextPlotLimitsY(
|
||||||
selected_y_axis,
|
limits.Min,
|
||||||
);
|
limits.Max,
|
||||||
}
|
*condition as sys::ImGuiCond,
|
||||||
}
|
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,28 +354,36 @@ 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())
|
||||||
pointer_vec = labels_value
|
.zip(self.show_y_default_ticks.iter())
|
||||||
.iter()
|
.enumerate()
|
||||||
.map(|x| x.as_ptr() as *const i8)
|
.for_each(|(k, ((positions, labels), show_defaults))| {
|
||||||
.collect::<Vec<*const i8>>();
|
if positions.is_some() && positions.as_ref().unwrap().len() > 0 {
|
||||||
pointer_vec.as_mut_ptr()
|
// The vector of pointers we create has to have a longer lifetime
|
||||||
} else {
|
let mut pointer_vec;
|
||||||
std::ptr::null_mut()
|
let labels_pointer = if let Some(labels_value) = &labels {
|
||||||
};
|
pointer_vec = labels_value
|
||||||
|
.iter()
|
||||||
|
.map(|x| x.as_ptr() as *const i8)
|
||||||
|
.collect::<Vec<*const i8>>();
|
||||||
|
pointer_vec.as_mut_ptr()
|
||||||
|
} else {
|
||||||
|
std::ptr::null_mut()
|
||||||
|
};
|
||||||
|
|
||||||
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],
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue