use crate::{AxisFlags, PlotCond, XAxis, YAxis, sys};
use dear_imgui_rs::{
Context as ImGuiContext, Ui, with_scratch_txt, with_scratch_txt_slice, with_scratch_txt_two,
};
use dear_imgui_sys as imgui_sys;
use std::os::raw::c_char;
use std::{cell::RefCell, rc::Rc};
pub struct PlotContext {
raw: *mut sys::ImPlotContext,
imgui_ctx_raw: *mut imgui_sys::ImGuiContext,
imgui_alive: Option<dear_imgui_rs::ContextAliveToken>,
}
impl PlotContext {
pub fn try_create(imgui_ctx: &ImGuiContext) -> dear_imgui_rs::ImGuiResult<Self> {
let imgui_ctx_raw = imgui_ctx.as_raw();
let imgui_alive = Some(imgui_ctx.alive_token());
assert_eq!(
unsafe { imgui_sys::igGetCurrentContext() },
imgui_ctx_raw,
"dear-implot: PlotContext must be created with the currently-active ImGui context"
);
unsafe { sys::ImPlot_SetImGuiContext(imgui_ctx_raw) };
let raw = unsafe { sys::ImPlot_CreateContext() };
if raw.is_null() {
return Err(dear_imgui_rs::ImGuiError::context_creation(
"ImPlot_CreateContext returned null",
));
}
unsafe { sys::ImPlot_SetCurrentContext(raw) };
Ok(Self {
raw,
imgui_ctx_raw,
imgui_alive,
})
}
pub fn create(imgui_ctx: &ImGuiContext) -> Self {
Self::try_create(imgui_ctx).expect("Failed to create ImPlot context")
}
pub fn current() -> Option<Self> {
let raw = unsafe { sys::ImPlot_GetCurrentContext() };
if raw.is_null() {
None
} else {
Some(Self {
raw,
imgui_ctx_raw: unsafe { imgui_sys::igGetCurrentContext() },
imgui_alive: None,
})
}
}
pub fn set_as_current(&self) {
if let Some(alive) = &self.imgui_alive {
assert!(
alive.is_alive(),
"dear-implot: ImGui context has been dropped"
);
unsafe { sys::ImPlot_SetImGuiContext(self.imgui_ctx_raw) };
}
unsafe {
sys::ImPlot_SetCurrentContext(self.raw);
}
}
pub fn get_plot_ui<'ui>(&'ui self, ui: &'ui Ui) -> PlotUi<'ui> {
if let Some(alive) = &self.imgui_alive {
assert!(
alive.is_alive(),
"dear-implot: ImGui context has been dropped"
);
assert_eq!(
unsafe { imgui_sys::igGetCurrentContext() },
self.imgui_ctx_raw,
"dear-implot: PlotUi must be used with the currently-active ImGui context"
);
}
self.set_as_current();
PlotUi { context: self, ui }
}
pub unsafe fn raw(&self) -> *mut sys::ImPlotContext {
self.raw
}
}
impl Drop for PlotContext {
fn drop(&mut self) {
if !self.raw.is_null() {
if let Some(alive) = &self.imgui_alive {
if !alive.is_alive() {
return;
}
unsafe { sys::ImPlot_SetImGuiContext(self.imgui_ctx_raw) };
}
unsafe {
if sys::ImPlot_GetCurrentContext() == self.raw {
sys::ImPlot_SetCurrentContext(std::ptr::null_mut());
}
sys::ImPlot_DestroyContext(self.raw);
}
}
}
}
pub struct PlotUi<'ui> {
#[allow(dead_code)]
context: &'ui PlotContext,
#[allow(dead_code)]
ui: &'ui Ui,
}
impl<'ui> PlotUi<'ui> {
pub fn begin_plot(&self, title: &str) -> Option<PlotToken<'_>> {
let size = sys::ImVec2_c { x: -1.0, y: 0.0 };
if title.contains('\0') {
return None;
}
let started = with_scratch_txt(title, |ptr| unsafe { sys::ImPlot_BeginPlot(ptr, size, 0) });
if started {
Some(PlotToken::new())
} else {
None
}
}
pub fn begin_plot_with_size(&self, title: &str, size: [f32; 2]) -> Option<PlotToken<'_>> {
let plot_size = sys::ImVec2_c {
x: size[0],
y: size[1],
};
if title.contains('\0') {
return None;
}
let started = with_scratch_txt(title, |ptr| unsafe {
sys::ImPlot_BeginPlot(ptr, plot_size, 0)
});
if started {
Some(PlotToken::new())
} else {
None
}
}
pub fn plot_line(&self, label: &str, x_data: &[f64], y_data: &[f64]) {
if x_data.len() != y_data.len() {
return; }
let count = match i32::try_from(x_data.len()) {
Ok(v) => v,
Err(_) => return,
};
let label = if label.contains('\0') { "" } else { label };
with_scratch_txt(label, |ptr| unsafe {
let spec = crate::plots::plot_spec_from(0, 0, std::mem::size_of::<f64>() as i32);
sys::ImPlot_PlotLine_doublePtrdoublePtr(
ptr,
x_data.as_ptr(),
y_data.as_ptr(),
count,
spec,
);
})
}
pub fn plot_scatter(&self, label: &str, x_data: &[f64], y_data: &[f64]) {
if x_data.len() != y_data.len() {
return; }
let count = match i32::try_from(x_data.len()) {
Ok(v) => v,
Err(_) => return,
};
let label = if label.contains('\0') { "" } else { label };
with_scratch_txt(label, |ptr| unsafe {
let spec = crate::plots::plot_spec_from(0, 0, std::mem::size_of::<f64>() as i32);
sys::ImPlot_PlotScatter_doublePtrdoublePtr(
ptr,
x_data.as_ptr(),
y_data.as_ptr(),
count,
spec,
);
})
}
pub fn plot_polygon(&self, label: &str, x_data: &[f64], y_data: &[f64]) {
if x_data.len() != y_data.len() {
return;
}
let count = match i32::try_from(x_data.len()) {
Ok(v) => v,
Err(_) => return,
};
let label = if label.contains('\0') { "" } else { label };
with_scratch_txt(label, |ptr| unsafe {
let spec = crate::plots::plot_spec_from(0, 0, std::mem::size_of::<f64>() as i32);
sys::ImPlot_PlotPolygon_doublePtr(ptr, x_data.as_ptr(), y_data.as_ptr(), count, spec);
})
}
pub fn is_plot_hovered(&self) -> bool {
unsafe { sys::ImPlot_IsPlotHovered() }
}
pub fn get_plot_mouse_pos(&self, y_axis: Option<crate::YAxisChoice>) -> sys::ImPlotPoint {
let y_axis_i32 = crate::y_axis_choice_option_to_i32(y_axis);
let y_axis = match y_axis_i32 {
0 => 3,
1 => 4,
2 => 5,
_ => 3,
};
unsafe { sys::ImPlot_GetPlotMousePos(0, y_axis) }
}
pub fn get_plot_mouse_pos_axes(&self, x_axis: XAxis, y_axis: YAxis) -> sys::ImPlotPoint {
unsafe { sys::ImPlot_GetPlotMousePos(x_axis as i32, y_axis as i32) }
}
pub fn set_axes(&self, x_axis: XAxis, y_axis: YAxis) {
unsafe { sys::ImPlot_SetAxes(x_axis as i32, y_axis as i32) }
}
pub fn setup_x_axis(&self, axis: XAxis, label: Option<&str>, flags: AxisFlags) {
let label = label.filter(|s| !s.contains('\0'));
match label {
Some(label) => with_scratch_txt(label, |ptr| unsafe {
sys::ImPlot_SetupAxis(
axis as sys::ImAxis,
ptr,
flags.bits() as sys::ImPlotAxisFlags,
)
}),
None => unsafe {
sys::ImPlot_SetupAxis(
axis as sys::ImAxis,
std::ptr::null(),
flags.bits() as sys::ImPlotAxisFlags,
)
},
}
}
pub fn setup_y_axis(&self, axis: YAxis, label: Option<&str>, flags: AxisFlags) {
let label = label.filter(|s| !s.contains('\0'));
match label {
Some(label) => with_scratch_txt(label, |ptr| unsafe {
sys::ImPlot_SetupAxis(
axis as sys::ImAxis,
ptr,
flags.bits() as sys::ImPlotAxisFlags,
)
}),
None => unsafe {
sys::ImPlot_SetupAxis(
axis as sys::ImAxis,
std::ptr::null(),
flags.bits() as sys::ImPlotAxisFlags,
)
},
}
}
pub fn setup_x_axis_limits(&self, axis: XAxis, min: f64, max: f64, cond: PlotCond) {
unsafe {
sys::ImPlot_SetupAxisLimits(axis as sys::ImAxis, min, max, cond as sys::ImPlotCond)
}
}
pub fn setup_y_axis_limits(&self, axis: YAxis, min: f64, max: f64, cond: PlotCond) {
unsafe {
sys::ImPlot_SetupAxisLimits(axis as sys::ImAxis, min, max, cond as sys::ImPlotCond)
}
}
pub fn setup_axis_links(
&self,
axis: i32,
link_min: Option<&mut f64>,
link_max: Option<&mut f64>,
) {
let pmin = link_min.map_or(std::ptr::null_mut(), |r| r as *mut f64);
let pmax = link_max.map_or(std::ptr::null_mut(), |r| r as *mut f64);
unsafe { sys::ImPlot_SetupAxisLinks(axis, pmin, pmax) }
}
pub fn setup_axes(
&self,
x_label: Option<&str>,
y_label: Option<&str>,
x_flags: AxisFlags,
y_flags: AxisFlags,
) {
let x_label = x_label.filter(|s| !s.contains('\0'));
let y_label = y_label.filter(|s| !s.contains('\0'));
match (x_label, y_label) {
(Some(x_label), Some(y_label)) => {
with_scratch_txt_two(x_label, y_label, |xp, yp| unsafe {
sys::ImPlot_SetupAxes(
xp,
yp,
x_flags.bits() as sys::ImPlotAxisFlags,
y_flags.bits() as sys::ImPlotAxisFlags,
)
})
}
(Some(x_label), None) => with_scratch_txt(x_label, |xp| unsafe {
sys::ImPlot_SetupAxes(
xp,
std::ptr::null(),
x_flags.bits() as sys::ImPlotAxisFlags,
y_flags.bits() as sys::ImPlotAxisFlags,
)
}),
(None, Some(y_label)) => with_scratch_txt(y_label, |yp| unsafe {
sys::ImPlot_SetupAxes(
std::ptr::null(),
yp,
x_flags.bits() as sys::ImPlotAxisFlags,
y_flags.bits() as sys::ImPlotAxisFlags,
)
}),
(None, None) => unsafe {
sys::ImPlot_SetupAxes(
std::ptr::null(),
std::ptr::null(),
x_flags.bits() as sys::ImPlotAxisFlags,
y_flags.bits() as sys::ImPlotAxisFlags,
)
},
}
}
pub fn setup_axes_limits(
&self,
x_min: f64,
x_max: f64,
y_min: f64,
y_max: f64,
cond: PlotCond,
) {
unsafe { sys::ImPlot_SetupAxesLimits(x_min, x_max, y_min, y_max, cond as sys::ImPlotCond) }
}
pub fn setup_finish(&self) {
unsafe { sys::ImPlot_SetupFinish() }
}
pub fn set_next_x_axis_limits(&self, axis: XAxis, min: f64, max: f64, cond: PlotCond) {
unsafe {
sys::ImPlot_SetNextAxisLimits(axis as sys::ImAxis, min, max, cond as sys::ImPlotCond)
}
}
pub fn set_next_y_axis_limits(&self, axis: YAxis, min: f64, max: f64, cond: PlotCond) {
unsafe {
sys::ImPlot_SetNextAxisLimits(axis as sys::ImAxis, min, max, cond as sys::ImPlotCond)
}
}
pub fn set_next_axis_links(
&self,
axis: i32,
link_min: Option<&mut f64>,
link_max: Option<&mut f64>,
) {
let pmin = link_min.map_or(std::ptr::null_mut(), |r| r as *mut f64);
let pmax = link_max.map_or(std::ptr::null_mut(), |r| r as *mut f64);
unsafe { sys::ImPlot_SetNextAxisLinks(axis, pmin, pmax) }
}
pub fn set_next_axes_limits(
&self,
x_min: f64,
x_max: f64,
y_min: f64,
y_max: f64,
cond: PlotCond,
) {
unsafe {
sys::ImPlot_SetNextAxesLimits(x_min, x_max, y_min, y_max, cond as sys::ImPlotCond)
}
}
pub fn set_next_axes_to_fit(&self) {
unsafe { sys::ImPlot_SetNextAxesToFit() }
}
pub fn set_next_axis_to_fit(&self, axis: i32) {
unsafe { sys::ImPlot_SetNextAxisToFit(axis as sys::ImAxis) }
}
pub fn set_next_x_axis_to_fit(&self, axis: XAxis) {
unsafe { sys::ImPlot_SetNextAxisToFit(axis as sys::ImAxis) }
}
pub fn set_next_y_axis_to_fit(&self, axis: YAxis) {
unsafe { sys::ImPlot_SetNextAxisToFit(axis as sys::ImAxis) }
}
pub fn setup_x_axis_ticks_positions(
&self,
axis: XAxis,
values: &[f64],
labels: Option<&[&str]>,
keep_default: bool,
) {
let count = match i32::try_from(values.len()) {
Ok(v) => v,
Err(_) => return,
};
if let Some(labels) = labels {
if labels.len() != values.len() {
return;
}
let cleaned: Vec<&str> = labels
.iter()
.map(|&s| if s.contains('\0') { "" } else { s })
.collect();
with_scratch_txt_slice(&cleaned, |ptrs| unsafe {
sys::ImPlot_SetupAxisTicks_doublePtr(
axis as sys::ImAxis,
values.as_ptr(),
count,
ptrs.as_ptr() as *const *const c_char,
keep_default,
)
})
} else {
unsafe {
sys::ImPlot_SetupAxisTicks_doublePtr(
axis as sys::ImAxis,
values.as_ptr(),
count,
std::ptr::null(),
keep_default,
)
}
}
}
pub fn setup_y_axis_ticks_positions(
&self,
axis: YAxis,
values: &[f64],
labels: Option<&[&str]>,
keep_default: bool,
) {
let count = match i32::try_from(values.len()) {
Ok(v) => v,
Err(_) => return,
};
if let Some(labels) = labels {
if labels.len() != values.len() {
return;
}
let cleaned: Vec<&str> = labels
.iter()
.map(|&s| if s.contains('\0') { "" } else { s })
.collect();
with_scratch_txt_slice(&cleaned, |ptrs| unsafe {
sys::ImPlot_SetupAxisTicks_doublePtr(
axis as sys::ImAxis,
values.as_ptr(),
count,
ptrs.as_ptr() as *const *const c_char,
keep_default,
)
})
} else {
unsafe {
sys::ImPlot_SetupAxisTicks_doublePtr(
axis as sys::ImAxis,
values.as_ptr(),
count,
std::ptr::null(),
keep_default,
)
}
}
}
pub fn setup_x_axis_ticks_range(
&self,
axis: XAxis,
v_min: f64,
v_max: f64,
n_ticks: i32,
labels: Option<&[&str]>,
keep_default: bool,
) {
if n_ticks <= 0 {
return;
}
if let Some(labels) = labels {
let Ok(ticks_usize) = usize::try_from(n_ticks) else {
return;
};
if labels.len() != ticks_usize {
return;
}
let cleaned: Vec<&str> = labels
.iter()
.map(|&s| if s.contains('\0') { "" } else { s })
.collect();
with_scratch_txt_slice(&cleaned, |ptrs| unsafe {
sys::ImPlot_SetupAxisTicks_double(
axis as sys::ImAxis,
v_min,
v_max,
n_ticks,
ptrs.as_ptr() as *const *const c_char,
keep_default,
)
})
} else {
unsafe {
sys::ImPlot_SetupAxisTicks_double(
axis as sys::ImAxis,
v_min,
v_max,
n_ticks,
std::ptr::null(),
keep_default,
)
}
}
}
pub fn setup_y_axis_ticks_range(
&self,
axis: YAxis,
v_min: f64,
v_max: f64,
n_ticks: i32,
labels: Option<&[&str]>,
keep_default: bool,
) {
if n_ticks <= 0 {
return;
}
if let Some(labels) = labels {
let Ok(ticks_usize) = usize::try_from(n_ticks) else {
return;
};
if labels.len() != ticks_usize {
return;
}
let cleaned: Vec<&str> = labels
.iter()
.map(|&s| if s.contains('\0') { "" } else { s })
.collect();
with_scratch_txt_slice(&cleaned, |ptrs| unsafe {
sys::ImPlot_SetupAxisTicks_double(
axis as sys::ImAxis,
v_min,
v_max,
n_ticks,
ptrs.as_ptr() as *const *const c_char,
keep_default,
)
})
} else {
unsafe {
sys::ImPlot_SetupAxisTicks_double(
axis as sys::ImAxis,
v_min,
v_max,
n_ticks,
std::ptr::null(),
keep_default,
)
}
}
}
pub fn setup_x_axis_format(&self, axis: XAxis, fmt: &str) {
if fmt.contains('\0') {
return;
}
with_scratch_txt(fmt, |ptr| unsafe {
sys::ImPlot_SetupAxisFormat_Str(axis as sys::ImAxis, ptr)
})
}
pub fn setup_y_axis_format(&self, axis: YAxis, fmt: &str) {
if fmt.contains('\0') {
return;
}
with_scratch_txt(fmt, |ptr| unsafe {
sys::ImPlot_SetupAxisFormat_Str(axis as sys::ImAxis, ptr)
})
}
pub fn setup_x_axis_scale(&self, axis: XAxis, scale: sys::ImPlotScale) {
unsafe { sys::ImPlot_SetupAxisScale_PlotScale(axis as sys::ImAxis, scale) }
}
pub fn setup_y_axis_scale(&self, axis: YAxis, scale: sys::ImPlotScale) {
unsafe { sys::ImPlot_SetupAxisScale_PlotScale(axis as sys::ImAxis, scale) }
}
pub fn setup_axis_limits_constraints(&self, axis: i32, v_min: f64, v_max: f64) {
unsafe { sys::ImPlot_SetupAxisLimitsConstraints(axis as sys::ImAxis, v_min, v_max) }
}
pub fn setup_axis_zoom_constraints(&self, axis: i32, z_min: f64, z_max: f64) {
unsafe { sys::ImPlot_SetupAxisZoomConstraints(axis as sys::ImAxis, z_min, z_max) }
}
pub fn setup_x_axis_format_closure<F>(&self, axis: XAxis, f: F) -> AxisFormatterToken
where
F: Fn(f64) -> String + Send + Sync + 'static,
{
AxisFormatterToken::new(axis as sys::ImAxis, f)
}
pub fn setup_y_axis_format_closure<F>(&self, axis: YAxis, f: F) -> AxisFormatterToken
where
F: Fn(f64) -> String + Send + Sync + 'static,
{
AxisFormatterToken::new(axis as sys::ImAxis, f)
}
pub fn setup_x_axis_transform_closure<FW, INV>(
&self,
axis: XAxis,
forward: FW,
inverse: INV,
) -> AxisTransformToken
where
FW: Fn(f64) -> f64 + Send + Sync + 'static,
INV: Fn(f64) -> f64 + Send + Sync + 'static,
{
AxisTransformToken::new(axis as sys::ImAxis, forward, inverse)
}
pub fn setup_y_axis_transform_closure<FW, INV>(
&self,
axis: YAxis,
forward: FW,
inverse: INV,
) -> AxisTransformToken
where
FW: Fn(f64) -> f64 + Send + Sync + 'static,
INV: Fn(f64) -> f64 + Send + Sync + 'static,
{
AxisTransformToken::new(axis as sys::ImAxis, forward, inverse)
}
}
#[derive(Default)]
struct PlotScopeStorage {
formatters: Vec<Box<FormatterHolder>>,
transforms: Vec<Box<TransformHolder>>,
}
thread_local! {
static PLOT_SCOPE_STACK: RefCell<Vec<PlotScopeStorage>> = const { RefCell::new(Vec::new()) };
}
fn with_plot_scope_storage<T>(f: impl FnOnce(&mut PlotScopeStorage) -> T) -> Option<T> {
PLOT_SCOPE_STACK.with(|stack| {
let mut stack = stack.borrow_mut();
stack.last_mut().map(f)
})
}
pub(crate) struct PlotScopeGuard {
_not_send_or_sync: std::marker::PhantomData<Rc<()>>,
}
impl PlotScopeGuard {
pub(crate) fn new() -> Self {
PLOT_SCOPE_STACK.with(|stack| stack.borrow_mut().push(PlotScopeStorage::default()));
Self {
_not_send_or_sync: std::marker::PhantomData,
}
}
}
impl Drop for PlotScopeGuard {
fn drop(&mut self) {
PLOT_SCOPE_STACK.with(|stack| {
let popped = stack.borrow_mut().pop();
debug_assert!(popped.is_some(), "dear-implot: plot scope stack underflow");
});
}
}
struct FormatterHolder {
func: Box<dyn Fn(f64) -> String + Send + Sync + 'static>,
}
#[must_use]
pub struct AxisFormatterToken {
_private: (),
}
impl AxisFormatterToken {
fn new<F>(axis: sys::ImAxis, f: F) -> Self
where
F: Fn(f64) -> String + Send + Sync + 'static,
{
let configured = with_plot_scope_storage(|storage| {
let holder = Box::new(FormatterHolder { func: Box::new(f) });
let user = &*holder as *const FormatterHolder as *mut std::os::raw::c_void;
storage.formatters.push(holder);
unsafe {
sys::ImPlot_SetupAxisFormat_PlotFormatter(
axis as sys::ImAxis,
Some(formatter_thunk),
user,
)
}
})
.is_some();
debug_assert!(
configured,
"dear-implot: axis formatter closure must be set within an active plot"
);
Self { _private: () }
}
}
impl Drop for AxisFormatterToken {
fn drop(&mut self) {
}
}
unsafe extern "C" fn formatter_thunk(
value: f64,
buff: *mut std::os::raw::c_char,
size: std::os::raw::c_int,
user_data: *mut std::os::raw::c_void,
) -> std::os::raw::c_int {
if user_data.is_null() || buff.is_null() || size <= 0 {
return 0;
}
let holder = unsafe { &*(user_data as *const FormatterHolder) };
let s = match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| (holder.func)(value))) {
Ok(v) => v,
Err(_) => {
eprintln!("dear-implot: panic in axis formatter callback");
std::process::abort();
}
};
let bytes = s.as_bytes();
let max = (size - 1).max(0) as usize;
let n = bytes.len().min(max);
unsafe {
std::ptr::copy_nonoverlapping(bytes.as_ptr(), buff as *mut u8, n);
*buff.add(n) = 0;
}
n as std::os::raw::c_int
}
struct TransformHolder {
forward: Box<dyn Fn(f64) -> f64 + Send + Sync + 'static>,
inverse: Box<dyn Fn(f64) -> f64 + Send + Sync + 'static>,
}
#[must_use]
pub struct AxisTransformToken {
_private: (),
}
impl AxisTransformToken {
fn new<FW, INV>(axis: sys::ImAxis, forward: FW, inverse: INV) -> Self
where
FW: Fn(f64) -> f64 + Send + Sync + 'static,
INV: Fn(f64) -> f64 + Send + Sync + 'static,
{
let configured = with_plot_scope_storage(|storage| {
let holder = Box::new(TransformHolder {
forward: Box::new(forward),
inverse: Box::new(inverse),
});
let user = &*holder as *const TransformHolder as *mut std::os::raw::c_void;
storage.transforms.push(holder);
unsafe {
sys::ImPlot_SetupAxisScale_PlotTransform(
axis as sys::ImAxis,
Some(transform_forward_thunk),
Some(transform_inverse_thunk),
user,
)
}
})
.is_some();
debug_assert!(
configured,
"dear-implot: axis transform closure must be set within an active plot"
);
Self { _private: () }
}
}
impl Drop for AxisTransformToken {
fn drop(&mut self) {
}
}
unsafe extern "C" fn transform_forward_thunk(
value: f64,
user_data: *mut std::os::raw::c_void,
) -> f64 {
if user_data.is_null() {
return value;
}
let holder = unsafe { &*(user_data as *const TransformHolder) };
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| (holder.forward)(value))) {
Ok(v) => v,
Err(_) => {
eprintln!("dear-implot: panic in axis transform (forward) callback");
std::process::abort();
}
}
}
unsafe extern "C" fn transform_inverse_thunk(
value: f64,
user_data: *mut std::os::raw::c_void,
) -> f64 {
if user_data.is_null() {
return value;
}
let holder = unsafe { &*(user_data as *const TransformHolder) };
match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| (holder.inverse)(value))) {
Ok(v) => v,
Err(_) => {
eprintln!("dear-implot: panic in axis transform (inverse) callback");
std::process::abort();
}
}
}
pub struct PlotToken<'ui> {
_scope: PlotScopeGuard,
_lifetime: std::marker::PhantomData<&'ui ()>,
}
impl<'ui> PlotToken<'ui> {
pub(crate) fn new() -> Self {
Self {
_scope: PlotScopeGuard::new(),
_lifetime: std::marker::PhantomData,
}
}
pub fn end(self) {
}
}
impl<'ui> Drop for PlotToken<'ui> {
fn drop(&mut self) {
unsafe {
sys::ImPlot_EndPlot();
}
}
}