diff --git a/README.md b/README.md index 54bacc1..815a5db 100644 --- a/README.md +++ b/README.md @@ -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. +## 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 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. 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 -is still being built. +for plot creation as well a subset of the functionality for plots are implemented. +See below for an overview of the progress. - [x] "BeginPlot" - [x] Basic hello world @@ -37,23 +44,23 @@ is still being built. - [ ] Pie chart - [ ] Digital data - [ ] Plot customization - - [ ] Enums - - [x] Axis flags - - [ ] Markers - - [ ] Styling colors - - [ ] Styling variables - - [ ] Colormaps + - [x] Axis flags + - [x] Styling colors + - [ ] Markers + - [ ] Styling variables + - [ ] Colormaps - [ ] Plot querying - - [ ] is hovered - - [ ] mouse position - - [ ] mouse limits + - [x] is hovered + - [x] mouse position in plot + - [x] plot limits - [ ] is queried - [ ] GetPlotQuery - [ ] Utils - [x] Plot limit setting + - [ ] imgui-rs style safe push/pop stacks - [ ] Plot tick setting - [ ] Plot y axis setting for subsequent elements - - [ ] plot position and size reading + - [ ] Plot position and size reading - [ ] Pixel to plot position - [ ] Plot to pixel position - [ ] Push/pop plotclip rect (?) diff --git a/implot-examples/examples/hello_world.rs b/implot-examples/examples/hello_world.rs index 441b948..7762a9b 100644 --- a/implot-examples/examples/hello_world.rs +++ b/implot-examples/examples/hello_world.rs @@ -1,5 +1,11 @@ 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; @@ -20,13 +26,24 @@ fn main() { )); ui.checkbox(im_str!("Show demo"), &mut showing_demo); + // Create some containers for exfiltrating data from the closure below + let mut hover_pos: Option = None; + let mut plot_limits: Option = None; + // Draw a plot + push_style_color(&PlotColorElement::PLOT_BG, 1.0, 1.0, 1.0, 0.2); Plot::new("Demo plot") .size(400.0, 300.0) .x_label("awesome x label") .y_label("awesome y label") - .x_limits(0.0, 6.0, Condition::FirstUseEver) - .y_limits(-1.0, 3.0, Condition::FirstUseEver) + .x_limits(&ImPlotRange { Min: 0.0, Max: 6.0 }, Condition::FirstUseEver) + .y_limits( + &ImPlotRange { + Min: -1.0, + Max: 3.0, + }, + Condition::FirstUseEver, + ) .with_plot_flags(&(PlotFlags::DEFAULT)) .with_y_axis_flags(&(AxisFlags::DEFAULT | AxisFlags::INVERT)) .build(|| { @@ -43,7 +60,31 @@ fn main() { .with_pixel_offset(10.0, 30.0) .plot(2.0, 2.0, false); 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 { diff --git a/src/lib.rs b/src/lib.rs index 08605de..ded3277 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,10 +11,13 @@ pub extern crate implot_sys as sys; use bitflags::bitflags; use sys::imgui::im_str; 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_Y: f32 = 400.0; +// --- Enum definitions -------------------------------------------------------------------------- bitflags! { /// Window hover check option flags. Documentation copied from implot.h for convenience. #[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. /// /// `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 y_label: String, /// X axis limits, if present - x_limits: Option<[f64; 2]>, + x_limits: Option, /// Y axis limits, if present - y_limits: Option<[f64; 2]>, + y_limits: Option, /// Condition on which the x limits are set x_limit_condition: Option, /// 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 #[inline] - pub fn x_limits(mut self, x_min: f64, x_max: f64, condition: Condition) -> Self { - self.x_limits = Some([x_min, x_max]); + pub fn x_limits(mut self, limits: &ImPlotRange, condition: Condition) -> Self { + self.x_limits = Some(*limits); self.x_limit_condition = Some(condition); self } /// Set the y limits of the plot #[inline] - pub fn y_limits(mut self, y_min: f64, y_max: f64, condition: Condition) -> Self { - self.y_limits = Some([y_min, y_max]); + pub fn y_limits(mut self, limits: &ImPlotRange, condition: Condition) -> Self { + self.y_limits = Some(*limits); self.y_limit_condition = Some(condition); self } @@ -231,17 +272,18 @@ impl Plot { pub fn begin(&self) -> Option { if let (Some(limits), Some(condition)) = (self.x_limits, self.x_limit_condition) { 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) { // TODO(4bb4) allow for specification of multiple y limits, not just the first + let selected_y_axis = 0; unsafe { sys::ImPlot_SetNextPlotLimitsY( - limits[0], - limits[1], + limits.Min, + limits.Max, 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. pub struct PlotLine { /// 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 /// 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.