Added plot style colors and some misc utils. See comments.

- Also exposed the sys crate's ImPlotRange, ImPlotLimits and ImPlotPoint
  to the user as well as changing the limit-setting interface to using
  those. I'm not super sure about doing this yet though, because it is
  more verbose for no real reason to the user and for the purposes of
  the abstraction it may be better to add facade types anyway.

- There is no protection against calling things like `is_hovered` from
  outside a plot. Will need to think about how to make this impossible
  by design - we could for example pass an object to Plot's closure
  that has these methods, and that object is not created anywhere
  else.
This commit is contained in:
4bb4 2020-08-23 21:26:27 +02:00
parent 90f7dba1a7
commit a43a58d819
3 changed files with 163 additions and 25 deletions

View file

@ -12,13 +12,20 @@ https://github.com/Gekkio/imgui-rs/pull/339 makes it into a release.
The sys crate compiles implot, so a C++ compiler will also be required. The sys crate compiles implot, so a C++ compiler will also be required.
## 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 papercuts, feel free to open an issue.
## Status ## Status
Currently a work in progress. The author is open to collaboration, if you'd like to 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. help, feel free to reach out via a Github issue.
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 as adding lines to plots are implemented. Everything else for plot creation as well a subset of the functionality for plots are implemented.
is still being built. See below for an overview of the progress.
- [x] "BeginPlot" - [x] "BeginPlot"
- [x] Basic hello world - [x] Basic hello world
@ -37,23 +44,23 @@ is still being built.
- [ ] Pie chart - [ ] Pie chart
- [ ] Digital data - [ ] Digital data
- [ ] Plot customization - [ ] Plot customization
- [ ] Enums - [x] Axis flags
- [x] Axis flags - [x] Styling colors
- [ ] Markers - [ ] Markers
- [ ] Styling colors - [ ] Styling variables
- [ ] Styling variables - [ ] Colormaps
- [ ] Colormaps
- [ ] Plot querying - [ ] Plot querying
- [ ] is hovered - [x] is hovered
- [ ] mouse position - [x] mouse position in plot
- [ ] mouse limits - [x] plot limits
- [ ] is queried - [ ] is queried
- [ ] GetPlotQuery - [ ] GetPlotQuery
- [ ] Utils - [ ] Utils
- [x] Plot limit setting - [x] Plot limit setting
- [ ] imgui-rs style safe push/pop stacks
- [ ] Plot tick setting - [ ] Plot tick setting
- [ ] Plot y axis setting for subsequent elements - [ ] Plot y axis setting for subsequent elements
- [ ] 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 (?)

View file

@ -1,5 +1,11 @@
use imgui::*; use imgui::*;
use implot::{AxisFlags, Plot, PlotFlags, PlotLine, PlotText}; use implot::{
get_plot_limits, get_plot_mouse_position, is_plot_hovered, pop_style_color, push_style_color,
};
use implot::{
AxisFlags, ImPlotLimits, ImPlotPoint, ImPlotRange, Plot, PlotColorElement, PlotFlags, PlotLine,
PlotText,
};
mod support; mod support;
@ -20,13 +26,24 @@ fn main() {
)); ));
ui.checkbox(im_str!("Show demo"), &mut showing_demo); ui.checkbox(im_str!("Show demo"), &mut showing_demo);
// Create some containers for exfiltrating data from the closure below
let mut hover_pos: Option<ImPlotPoint> = None;
let mut plot_limits: Option<ImPlotLimits> = None;
// Draw a plot // Draw a plot
push_style_color(&PlotColorElement::PLOT_BG, 1.0, 1.0, 1.0, 0.2);
Plot::new("Demo plot") Plot::new("Demo plot")
.size(400.0, 300.0) .size(400.0, 300.0)
.x_label("awesome x label") .x_label("awesome x label")
.y_label("awesome y label") .y_label("awesome y label")
.x_limits(0.0, 6.0, Condition::FirstUseEver) .x_limits(&ImPlotRange { Min: 0.0, Max: 6.0 }, Condition::FirstUseEver)
.y_limits(-1.0, 3.0, Condition::FirstUseEver) .y_limits(
&ImPlotRange {
Min: -1.0,
Max: 3.0,
},
Condition::FirstUseEver,
)
.with_plot_flags(&(PlotFlags::DEFAULT)) .with_plot_flags(&(PlotFlags::DEFAULT))
.with_y_axis_flags(&(AxisFlags::DEFAULT | AxisFlags::INVERT)) .with_y_axis_flags(&(AxisFlags::DEFAULT | AxisFlags::INVERT))
.build(|| { .build(|| {
@ -43,7 +60,31 @@ fn main() {
.with_pixel_offset(10.0, 30.0) .with_pixel_offset(10.0, 30.0)
.plot(2.0, 2.0, false); .plot(2.0, 2.0, false);
PlotText::new("Vertical Text!").plot(0.1, 2.5, true); PlotText::new("Vertical Text!").plot(0.1, 2.5, true);
if is_plot_hovered() {
hover_pos = Some(get_plot_mouse_position());
}
plot_limits = Some(get_plot_limits());
}); });
// Print some previously-exfiltrated info. This is because calling
// things like is_plot_hovered or get_plot_mouse_position() outside
// of an actual Plot is not allowed.
if let Some(pos) = hover_pos {
ui.text(im_str!("hovered at {}, {}", pos.x, pos.y));
}
if let Some(limits) = plot_limits {
ui.text(im_str!(
"X limits are {:+10.3}, {:+10.3}",
limits.X.Min,
limits.X.Max
));
ui.text(im_str!(
"Y limits are {:+10.3}, {:+10.3}",
limits.Y.Min,
limits.Y.Max
));
}
pop_style_color(1);
}); });
if showing_demo { if showing_demo {

View file

@ -11,10 +11,13 @@ pub extern crate implot_sys as sys;
use bitflags::bitflags; use bitflags::bitflags;
use sys::imgui::im_str; use sys::imgui::im_str;
pub use sys::imgui::Condition; pub use sys::imgui::Condition;
// TODO(4bb4) facade-wrap these
pub use sys::{ImPlotLimits, ImPlotPoint, ImPlotRange};
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;
// --- Enum definitions --------------------------------------------------------------------------
bitflags! { bitflags! {
/// Window hover check option flags. Documentation copied from implot.h for convenience. /// Window hover check option flags. Documentation copied from implot.h for convenience.
#[repr(transparent)] #[repr(transparent)]
@ -78,6 +81,44 @@ bitflags! {
} }
} }
bitflags! {
/// Colorable plot elements. These are called "ImPlotCol" in ImPlot itself, but I found that
/// name somewhat confusing because we are not referring to colors, but _which_ thing can
/// be colored - hence I added the "Element".
#[repr(transparent)]
pub struct PlotColorElement: u32 {
/// Plot line/outline color (defaults to next unused color in current colormap)
const LINE = sys::ImPlotCol__ImPlotCol_Line;
/// Plot fill color for bars (defaults to the current line color)
const FILL = sys::ImPlotCol__ImPlotCol_Fill;
/// Marker outline color (defaults to the current line color)
const MARKER_OUTLINE = sys::ImPlotCol__ImPlotCol_MarkerOutline;
/// Marker fill color (defaults to the current line color)
const MARKER_FILL = sys::ImPlotCol__ImPlotCol_MarkerFill;
/// Error bar color (defaults to text color)
const ERROR_BAR = sys::ImPlotCol__ImPlotCol_ErrorBar;
/// Plot frame background color (defaults to FRAME_BG)
const FRAME_BG = sys::ImPlotCol__ImPlotCol_FrameBg;
/// Plot area background color (defaults to WINDOW_BG)
const PLOT_BG = sys::ImPlotCol__ImPlotCol_PlotBg;
/// Plot area border color (defaults to text color)
const PLOT_BORDER = sys::ImPlotCol__ImPlotCol_PlotBorder;
/// X-axis grid/label color (defaults to 25% text color)
const X_AXIS = sys::ImPlotCol__ImPlotCol_XAxis;
/// Y-axis grid/label color (defaults to 25% text color)
const Y_AXIS = sys::ImPlotCol__ImPlotCol_YAxis;
/// 2nd y-axis grid/label color (defaults to 25% text color)
const Y_AXIS2 = sys::ImPlotCol__ImPlotCol_YAxis2;
/// 3rd y-axis grid/label color (defaults to 25% text color)
const Y_AXIS3 = sys::ImPlotCol__ImPlotCol_YAxis3;
/// Box-selection color (defaults to yellow)
const SELECTION = sys::ImPlotCol__ImPlotCol_Selection;
/// Box-query color (defaults to green)
const QUERY = sys::ImPlotCol__ImPlotCol_Query;
}
}
// --- Main plot structure -----------------------------------------------------------------------
/// Struct to represent an ImPlot. This is the main construct used to contain all kinds of plots in ImPlot. /// Struct to represent an ImPlot. This is the main construct used to contain all kinds of plots in ImPlot.
/// ///
/// `Plot` is to be used (within an imgui window) with the following pattern: /// `Plot` is to be used (within an imgui window) with the following pattern:
@ -104,9 +145,9 @@ pub struct Plot {
/// Label of the y axis, shown on the left /// Label of the y axis, shown on the left
y_label: String, y_label: String,
/// X axis limits, if present /// X axis limits, if present
x_limits: Option<[f64; 2]>, x_limits: Option<ImPlotRange>,
/// Y axis limits, if present /// Y axis limits, if present
y_limits: Option<[f64; 2]>, y_limits: Option<ImPlotRange>,
/// 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 (first y axis for now)
@ -172,16 +213,16 @@ impl Plot {
/// Set the x limits of the plot /// Set the x limits of the plot
#[inline] #[inline]
pub fn x_limits(mut self, x_min: f64, x_max: f64, condition: Condition) -> Self { pub fn x_limits(mut self, limits: &ImPlotRange, condition: Condition) -> Self {
self.x_limits = Some([x_min, x_max]); self.x_limits = Some(*limits);
self.x_limit_condition = Some(condition); self.x_limit_condition = Some(condition);
self self
} }
/// Set the y limits of the plot /// Set the y limits of the plot
#[inline] #[inline]
pub fn y_limits(mut self, y_min: f64, y_max: f64, condition: Condition) -> Self { pub fn y_limits(mut self, limits: &ImPlotRange, condition: Condition) -> Self {
self.y_limits = Some([y_min, y_max]); self.y_limits = Some(*limits);
self.y_limit_condition = Some(condition); self.y_limit_condition = Some(condition);
self self
} }
@ -231,17 +272,18 @@ impl Plot {
pub fn begin(&self) -> Option<PlotToken> { pub fn begin(&self) -> Option<PlotToken> {
if let (Some(limits), Some(condition)) = (self.x_limits, self.x_limit_condition) { if let (Some(limits), Some(condition)) = (self.x_limits, self.x_limit_condition) {
unsafe { unsafe {
sys::ImPlot_SetNextPlotLimitsX(limits[0], limits[1], condition as sys::ImGuiCond); sys::ImPlot_SetNextPlotLimitsX(limits.Min, limits.Max, condition as sys::ImGuiCond);
} }
} }
if let (Some(limits), Some(condition)) = (self.y_limits, self.y_limit_condition) { if let (Some(limits), Some(condition)) = (self.y_limits, self.y_limit_condition) {
// TODO(4bb4) allow for specification of multiple y limits, not just the first // TODO(4bb4) allow for specification of multiple y limits, not just the first
let selected_y_axis = 0;
unsafe { unsafe {
sys::ImPlot_SetNextPlotLimitsY( sys::ImPlot_SetNextPlotLimitsY(
limits[0], limits.Min,
limits[1], limits.Max,
condition as sys::ImGuiCond, condition as sys::ImGuiCond,
0, selected_y_axis,
); );
} }
} }
@ -313,6 +355,7 @@ impl Drop for PlotToken {
} }
} }
// --- Actual plotting functionality -------------------------------------------------------------
/// Struct to provide functionality for plotting a line in a plot. /// Struct to provide functionality for plotting a line in a plot.
pub struct PlotLine { pub struct PlotLine {
/// Label to show in the legend for this line /// Label to show in the legend for this line
@ -401,6 +444,53 @@ impl PlotText {
} }
} }
// --- Push/pop utils -------------------------------------------------------------------------
// Currently not in a struct yet. imgui-rs has some smarts about dealing with stacks, in particular
// leak detection, which I'd like to replicate here at some point.
/// Push a style color to the stack, giving an element and the four components of the color.
/// The components should be between 0.0 (no intensity) and 1.0 (full intensity)
pub fn push_style_color(element: &PlotColorElement, red: f32, green: f32, blue: f32, alpha: f32) {
// TODO this is actually unsafe, safe-wrap this like in imgui-rs' stacks.rs
unsafe {
sys::ImPlot_PushStyleColorVec4(
element.bits() as sys::ImPlotCol,
sys::ImVec4 {
x: red,
y: green,
z: blue,
w: alpha,
},
);
}
}
/// Pop a given number of previously-pushed style color from the stack.
pub fn pop_style_color(count: i32) {
// TODO this is actually unsafe, safe-wrap this like in imgui-rs' stacks.rs
unsafe { sys::ImPlot_PopStyleColor(count) }
}
// --- Miscellaneous -----------------------------------------------------------------------------
/// Returns true if the plot area in the current or most recent plot is hovered.
pub fn is_plot_hovered() -> bool {
unsafe { sys::ImPlot_IsPlotHovered() }
}
/// Returns the mouse position in x,y coordinates of the current or most recent plot. Currently
/// pertains to whatever Y axis was most recently selected. TODO(4bb4) add y axis selection
pub fn get_plot_mouse_position() -> ImPlotPoint {
let y_axis_selection = 0;
unsafe { sys::ImPlot_GetPlotMousePos(y_axis_selection) }
}
/// Returns the current or most recent plot axis range. Currently pertains to whatever Y axis was
/// most recently selected. TODO(4bb4) add y axis selection
pub fn get_plot_limits() -> ImPlotLimits {
let y_axis_selection = 0;
unsafe { sys::ImPlot_GetPlotLimits(y_axis_selection) }
}
// --- 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
/// already - if you find something missing you'd really like, raise an issue. /// already - if you find something missing you'd really like, raise an issue.