diff --git a/src/heatmap.rs b/src/heatmap.rs new file mode 100644 index 0000000..2a023b7 --- /dev/null +++ b/src/heatmap.rs @@ -0,0 +1,47 @@ +use sys::{ImPlotHeatmapFlags, ImPlotHeatmapFlags__ImPlotHeatmapFlags_ColMajor}; + +use crate::sys; +use std::ffi::CString; +use std::os::raw::c_char; + +pub use crate::sys::ImPlotPoint; + +pub struct PlotHeatmap { + label: CString, +} + +impl PlotHeatmap { + pub fn new(label: &str) -> Self { + Self { + label: CString::new(label) + .unwrap_or_else(|_| panic!("label string has internal null bytes: {}", label)), + } + } + + pub fn plot(&self, x: &[f64], rows: i32, cols: i32) { + if x.is_empty() { + return; + } + + unsafe { + sys::ImPlot_PlotHeatmap_doublePtr( + self.label.as_ptr() as *const c_char, + x.as_ptr(), + rows, + cols, + 0.0, + 0.0, + "\0".as_ptr() as *const c_char, + ImPlotPoint { + x: 0f64, + y: rows as f64, + }, + ImPlotPoint { + x: cols as f64, + y: 0f64, + }, + ImPlotHeatmapFlags__ImPlotHeatmapFlags_ColMajor as ImPlotHeatmapFlags, + ); + } + } +} diff --git a/src/lib.rs b/src/lib.rs index 89c96cc..ba9f653 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -24,6 +24,7 @@ use std::os::raw::c_char; pub use sys::{ImAxis, ImPlotPoint, ImPlotRange, ImPlotRect, ImVec2, ImVec4}; mod context; +pub mod heatmap; pub mod lines; mod plot; mod plot_elements; @@ -31,11 +32,11 @@ pub mod rect; // 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; +//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; +//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 diff --git a/src/lines.rs b/src/lines.rs index 912a979..756b413 100644 --- a/src/lines.rs +++ b/src/lines.rs @@ -16,6 +16,25 @@ impl PlotLine { } } + pub fn plot_with_step(&self, x: &[f64], step: f64) { + if x.is_empty() { + return; + } + + unsafe { + sys::ImPlot_PlotLine_doublePtrInt( + self.label.as_ptr() as *const c_char, + x.as_ptr(), + x.len() as i32, + step, + 0f64, + 0, + 0, + std::mem::size_of::() as i32, + ); + } + } + pub fn plot(&self, x: &[f64], y: &[f64]) { if x.len().min(y.len()) == 0 { return; diff --git a/src/plot.rs b/src/plot.rs index 84275b9..1e6ff5e 100644 --- a/src/plot.rs +++ b/src/plot.rs @@ -1,8 +1,8 @@ -use std::ffi::CString; +use std::ffi::{c_char, CString}; use bitflags::bitflags; use implot_sys as sys; -use sys::{ImAxis, ImVec2, *}; +use sys::{ImVec2, *}; use crate::{Context, PlotUi}; @@ -22,17 +22,17 @@ bitflags! { #[repr(transparent)] pub struct PlotFlags: u32 { const NONE = ImPlotFlags__ImPlotFlags_None; - const NOTITLE = ImPlotFlags__ImPlotFlags_NoTitle; - const NOLEGEND = ImPlotFlags__ImPlotFlags_NoLegend; - const NOMOUSETEXT = ImPlotFlags__ImPlotFlags_NoMouseText; - const NOINPUTS = ImPlotFlags__ImPlotFlags_NoInputs; - const NOMENUS = ImPlotFlags__ImPlotFlags_NoMenus; - const NOBOXSELECT = ImPlotFlags__ImPlotFlags_NoBoxSelect; - const NOCHILD = ImPlotFlags__ImPlotFlags_NoChild; - const NOFRAME = ImPlotFlags__ImPlotFlags_NoFrame; + const NO_TITLE = ImPlotFlags__ImPlotFlags_NoTitle; + const NO_LEGEND = ImPlotFlags__ImPlotFlags_NoLegend; + const NO_MOUSE_TEXT = ImPlotFlags__ImPlotFlags_NoMouseText; + const NO_INPUTS = ImPlotFlags__ImPlotFlags_NoInputs; + const NO_MENUS = ImPlotFlags__ImPlotFlags_NoMenus; + const NO_BOX_SELECT = ImPlotFlags__ImPlotFlags_NoBoxSelect; + const NO_CHILD = ImPlotFlags__ImPlotFlags_NoChild; + const NO_FRAME = ImPlotFlags__ImPlotFlags_NoFrame; const EQUAL = ImPlotFlags__ImPlotFlags_Equal; const CROSSHAIRS = ImPlotFlags__ImPlotFlags_Crosshairs; - const CANVASONLY = ImPlotFlags__ImPlotFlags_CanvasOnly; + const CANVAS_ONLY = ImPlotFlags__ImPlotFlags_CanvasOnly; } } @@ -70,6 +70,8 @@ pub struct Plot { flags: ImPlotFlags, x_flags: ImPlotAxisFlags, y_flags: ImPlotAxisFlags, + x_limit: Option<[f64; 2]>, + y_limit: Option<[f64; 2]>, } impl Plot { @@ -83,6 +85,25 @@ impl Plot { flags: PlotFlags::NONE.bits() as ImPlotFlags, x_flags: PlotAxisFlags::NONE.bits() as ImPlotAxisFlags, y_flags: PlotAxisFlags::NONE.bits() as ImPlotAxisFlags, + x_limit: None, + y_limit: None, + } + } + + pub fn color_map_scale(title: &str, min: f64, max: f64, size: ImVec2) { + let title = CString::new(title) + .unwrap_or_else(|_| panic!("string contains internal null bytes: {}", title)); + + unsafe { + sys::ImPlot_ColormapScale( + title.as_ptr() as *const c_char, + min, + max, + size, + "%.0f\0".as_ptr() as *const c_char, + 0, + 0, + ); } } @@ -91,21 +112,31 @@ impl Plot { self } - pub fn with_flags(mut self, flags: &PlotFlags) -> Self { + pub fn with_flags(mut self, flags: PlotFlags) -> Self { self.flags = flags.bits() as ImPlotFlags; self } - pub fn with_x_flags(mut self, flags: &PlotAxisFlags) -> Self { + pub fn with_x_flags(mut self, flags: PlotAxisFlags) -> Self { self.x_flags = flags.bits() as ImPlotAxisFlags; self } - pub fn with_y_flags(mut self, flags: &PlotAxisFlags) -> Self { + pub fn with_y_flags(mut self, flags: PlotAxisFlags) -> Self { self.y_flags = flags.bits() as ImPlotAxisFlags; self } + pub fn with_x_limit(mut self, min: f64, max: f64) -> Self { + self.x_limit = Some([min, max]); + self + } + + pub fn with_y_limit(mut self, min: f64, max: f64) -> Self { + self.y_limit = Some([min, max]); + self + } + pub fn x_label(mut self, label: &str) -> Self { self.x_label = CString::new(label) .unwrap_or_else(|_| panic!("string contains internal null bytes: {}", label)); @@ -127,15 +158,29 @@ impl Plot { y: self.size[1], }; - let should_render = sys::ImPlot_BeginPlot(self.title.as_ptr(), size_vec, self.flags); - - should_render + sys::ImPlot_BeginPlot(self.title.as_ptr(), size_vec, self.flags) }; if should_render { unsafe { sys::ImPlot_SetupAxis(Axis::X1 as i32, self.x_label.as_ptr(), self.x_flags); sys::ImPlot_SetupAxis(Axis::Y1 as i32, self.y_label.as_ptr(), self.y_flags); + if let Some(limit) = self.x_limit { + sys::ImPlot_SetupAxisLimits( + Axis::X1 as i32, + limit[0], + limit[1], + ImPlotCond__ImPlotCond_Once as i32, + ); + } + if let Some(limit) = self.y_limit { + sys::ImPlot_SetupAxisLimits( + Axis::Y1 as i32, + limit[0], + limit[1], + ImPlotCond__ImPlotCond_Once as i32, + ); + } } Some(PlotToken {