extern crate implot_sys as sys;
use bitflags::bitflags;
pub use sys::imgui::Condition;
use sys::imgui::{im_str, ImString};
pub use sys::{ImPlotLimits, ImPlotPoint, ImPlotRange, ImVec2, ImVec4};
use crate::{Context, PlotUi};
const DEFAULT_PLOT_SIZE_X: f32 = 400.0;
const DEFAULT_PLOT_SIZE_Y: f32 = 400.0;
bitflags! {
#[repr(transparent)]
pub struct PlotFlags: u32 {
const NONE = sys::ImPlotFlags__ImPlotFlags_None;
const NO_LEGEND = sys::ImPlotFlags__ImPlotFlags_NoLegend;
const NO_MENUS = sys::ImPlotFlags__ImPlotFlags_NoMenus;
const NO_BOX_SELECT = sys::ImPlotFlags__ImPlotFlags_NoBoxSelect;
const NO_MOUSE_POSITION = sys::ImPlotFlags__ImPlotFlags_NoMousePos;
const NO_HIGHLIGHT = sys::ImPlotFlags__ImPlotFlags_NoHighlight;
const NO_CHILD = sys::ImPlotFlags__ImPlotFlags_NoChild;
const Y_AXIS_2 = sys::ImPlotFlags__ImPlotFlags_YAxis2;
const Y_AXIS_3 = sys::ImPlotFlags__ImPlotFlags_YAxis3;
const QUERY = sys::ImPlotFlags__ImPlotFlags_Query;
const CROSSHAIRS = sys::ImPlotFlags__ImPlotFlags_Crosshairs;
const ANTIALIASED = sys::ImPlotFlags__ImPlotFlags_AntiAliased;
}
}
bitflags! {
#[repr(transparent)]
pub struct AxisFlags: u32 {
const NONE = sys::ImPlotAxisFlags__ImPlotAxisFlags_None;
const NO_GRID_LINES = sys::ImPlotAxisFlags__ImPlotAxisFlags_NoGridLines;
const NO_TICK_MARKS = sys::ImPlotAxisFlags__ImPlotAxisFlags_NoTickMarks;
const NO_TICK_LABELS = sys::ImPlotAxisFlags__ImPlotAxisFlags_NoTickLabels;
const LOG_SCALE = sys::ImPlotAxisFlags__ImPlotAxisFlags_LogScale;
const TIME = sys::ImPlotAxisFlags__ImPlotAxisFlags_Time;
const INVERT = sys::ImPlotAxisFlags__ImPlotAxisFlags_Invert;
const LOCK_MIN = sys::ImPlotAxisFlags__ImPlotAxisFlags_LockMin;
const LOCK_MAX = sys::ImPlotAxisFlags__ImPlotAxisFlags_LockMax;
}
}
pub struct Plot {
title: String,
size_x: f32,
size_y: f32,
x_label: String,
y_label: String,
x_limits: Option<ImPlotRange>,
y_limits: Option<ImPlotRange>,
x_limit_condition: Option<Condition>,
y_limit_condition: Option<Condition>,
x_tick_positions: Option<Vec<f64>>,
x_tick_labels: Option<Vec<ImString>>,
show_x_default_ticks: bool,
y_tick_positions: Option<Vec<f64>>,
y_tick_labels: Option<Vec<ImString>>,
show_y_default_ticks: bool,
plot_flags: sys::ImPlotFlags,
x_flags: sys::ImPlotAxisFlags,
y_flags: sys::ImPlotAxisFlags,
y2_flags: sys::ImPlotAxisFlags,
y3_flags: sys::ImPlotAxisFlags,
}
impl Plot {
pub fn new(title: &str) -> Self {
Self {
title: title.to_owned(),
size_x: DEFAULT_PLOT_SIZE_X,
size_y: DEFAULT_PLOT_SIZE_Y,
x_label: "".to_owned(),
y_label: "".to_owned(),
x_limits: None,
y_limits: None,
x_limit_condition: None,
y_limit_condition: None,
x_tick_positions: None,
x_tick_labels: None,
show_x_default_ticks: false,
y_tick_positions: None,
y_tick_labels: None,
show_y_default_ticks: false,
plot_flags: PlotFlags::NONE.bits() as sys::ImPlotFlags,
x_flags: AxisFlags::NONE.bits() as sys::ImPlotAxisFlags,
y_flags: AxisFlags::NONE.bits() as sys::ImPlotAxisFlags,
y2_flags: AxisFlags::NONE.bits() as sys::ImPlotAxisFlags,
y3_flags: AxisFlags::NONE.bits() as sys::ImPlotAxisFlags,
}
}
#[inline]
pub fn size(mut self, size_x: f32, size_y: f32) -> Self {
self.size_x = size_x;
self.size_y = size_y;
self
}
#[inline]
pub fn x_label(mut self, label: &str) -> Self {
self.x_label = label.to_owned();
self
}
#[inline]
pub fn y_label(mut self, label: &str) -> Self {
self.y_label = label.to_owned();
self
}
#[inline]
pub fn x_limits(mut self, limits: &ImPlotRange, condition: Condition) -> Self {
self.x_limits = Some(*limits);
self.x_limit_condition = Some(condition);
self
}
#[inline]
pub fn y_limits(mut self, limits: &ImPlotRange, condition: Condition) -> Self {
self.y_limits = Some(*limits);
self.y_limit_condition = Some(condition);
self
}
#[inline]
pub fn x_ticks(mut self, ticks: &Vec<f64>, show_default: bool) -> Self {
self.x_tick_positions = Some(ticks.clone());
self.show_x_default_ticks = show_default;
self
}
#[inline]
pub fn y_ticks(mut self, ticks: &Vec<f64>, show_default: bool) -> Self {
self.y_tick_positions = Some(ticks.clone());
self.show_y_default_ticks = show_default;
self
}
#[inline]
pub fn x_ticks_with_labels(
mut self,
tick_labels: &Vec<(f64, String)>,
show_default: bool,
) -> Self {
self.x_tick_positions = Some(tick_labels.iter().map(|x| x.0).collect());
self.x_tick_labels = Some(tick_labels.iter().map(|x| im_str!("{}", x.1)).collect());
self.show_x_default_ticks = show_default;
self
}
#[inline]
pub fn y_ticks_with_labels(
mut self,
tick_labels: &Vec<(f64, String)>,
show_default: bool,
) -> Self {
self.y_tick_positions = Some(tick_labels.iter().map(|x| x.0).collect());
self.y_tick_labels = Some(tick_labels.iter().map(|x| im_str!("{}", x.1)).collect());
self.show_y_default_ticks = show_default;
self
}
#[inline]
pub fn with_plot_flags(mut self, flags: &PlotFlags) -> Self {
self.plot_flags = flags.bits() as sys::ImPlotFlags;
self
}
#[inline]
pub fn with_x_axis_flags(mut self, flags: &AxisFlags) -> Self {
self.x_flags = flags.bits() as sys::ImPlotAxisFlags;
self
}
#[inline]
pub fn with_y_axis_flags(mut self, flags: &AxisFlags) -> Self {
self.y_flags = flags.bits() as sys::ImPlotAxisFlags;
self
}
#[inline]
pub fn with_y2_axis_flags(mut self, flags: &AxisFlags) -> Self {
self.y2_flags = flags.bits() as sys::ImPlotAxisFlags;
self
}
#[inline]
pub fn with_y3_axis_flags(mut self, flags: &AxisFlags) -> Self {
self.y3_flags = flags.bits() as sys::ImPlotAxisFlags;
self
}
fn maybe_set_axis_limits(&self) {
if let (Some(limits), Some(condition)) = (self.x_limits, self.x_limit_condition) {
unsafe {
sys::ImPlot_SetNextPlotLimitsX(limits.Min, limits.Max, condition as sys::ImGuiCond);
}
}
if let (Some(limits), Some(condition)) = (self.y_limits, self.y_limit_condition) {
let selected_y_axis = 0;
unsafe {
sys::ImPlot_SetNextPlotLimitsY(
limits.Min,
limits.Max,
condition as sys::ImGuiCond,
selected_y_axis,
);
}
}
}
fn maybe_set_tick_labels(&self) {
if self.x_tick_positions.is_some() && self.x_tick_positions.as_ref().unwrap().len() > 0 {
let mut pointer_vec; let labels_pointer = if let Some(labels_value) = &self.x_tick_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 {
sys::ImPlot_SetNextPlotTicksXdoublePtr(
self.x_tick_positions.as_ref().unwrap().as_ptr(),
self.x_tick_positions.as_ref().unwrap().len() as i32,
labels_pointer,
self.show_x_default_ticks,
)
}
}
if self.y_tick_positions.is_some() && self.y_tick_positions.as_ref().unwrap().len() > 0 {
let mut pointer_vec; let labels_pointer = if let Some(labels_value) = &self.y_tick_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 {
sys::ImPlot_SetNextPlotTicksYdoublePtr(
self.y_tick_positions.as_ref().unwrap().as_ptr(),
self.y_tick_positions.as_ref().unwrap().len() as i32,
labels_pointer,
self.show_y_default_ticks,
0, )
}
}
}
pub fn begin(&self, plot_ui: &PlotUi) -> Option<PlotToken> {
self.maybe_set_axis_limits();
self.maybe_set_tick_labels();
let should_render = unsafe {
sys::ImPlot_BeginPlot(
im_str!("{}", self.title).as_ptr(),
im_str!("{}", self.x_label).as_ptr(),
im_str!("{}", self.y_label).as_ptr(),
sys::ImVec2 {
x: self.size_x as f32,
y: self.size_y as f32,
},
self.plot_flags,
self.x_flags,
self.y_flags,
self.y2_flags,
self.y3_flags,
)
};
if should_render {
Some(PlotToken {
context: plot_ui.context,
plot_title: self.title.clone(),
})
} else {
None
}
}
pub fn build<F: FnOnce()>(self, plot_ui: &PlotUi, f: F) {
if let Some(token) = self.begin(plot_ui) {
f();
token.end()
}
}
}
pub struct PlotToken {
context: *const Context,
plot_title: String,
}
impl PlotToken {
pub fn end(mut self) {
self.context = std::ptr::null();
unsafe { sys::ImPlot_EndPlot() };
}
}
impl Drop for PlotToken {
fn drop(&mut self) {
if !self.context.is_null() && !std::thread::panicking() {
panic!(
"Warning: A PlotToken for plot \"{}\" was not called end() on",
self.plot_title
);
}
}
}