use dear_imgui_rs::sys as imgui_sys;
use dear_imgui_rs::texture::TextureRef;
pub use dear_imgui_rs::{Context, Ui};
use dear_implot3d_sys as sys;
mod flags;
mod item_style;
mod style;
mod ui_ext;
pub use flags::*;
pub use item_style::*;
pub use style::*;
pub use ui_ext::*;
pub mod meshes;
pub mod plots;
pub use plots::*;
use std::borrow::Cow;
use std::cell::RefCell;
fn len_i32(len: usize) -> Option<i32> {
i32::try_from(len).ok()
}
const IMPLOT3D_AUTO: i32 = -1;
thread_local! {
static NEXT_PLOT3D_SPEC: RefCell<Option<sys::ImPlot3DSpec_c>> = RefCell::new(None);
}
pub(crate) fn update_next_plot3d_spec(f: impl FnOnce(&mut sys::ImPlot3DSpec_c)) {
NEXT_PLOT3D_SPEC.with(|cell| {
let mut guard = cell.borrow_mut();
let mut spec = guard.take().unwrap_or_else(default_plot3d_spec);
f(&mut spec);
*guard = Some(spec);
})
}
fn take_next_plot3d_spec() -> Option<sys::ImPlot3DSpec_c> {
NEXT_PLOT3D_SPEC.with(|cell| cell.borrow_mut().take())
}
fn set_next_plot3d_spec(spec: Option<sys::ImPlot3DSpec_c>) {
NEXT_PLOT3D_SPEC.with(|cell| {
*cell.borrow_mut() = spec;
})
}
fn default_plot3d_spec() -> sys::ImPlot3DSpec_c {
let auto_col = sys::ImVec4_c {
x: 0.0,
y: 0.0,
z: 0.0,
w: -1.0,
};
sys::ImPlot3DSpec_c {
LineColor: auto_col,
LineColors: std::ptr::null_mut(),
LineWeight: 1.0,
FillColor: auto_col,
FillColors: std::ptr::null_mut(),
FillAlpha: -1.0,
Marker: sys::ImPlot3DMarker_Auto as _,
MarkerSize: -1.0,
MarkerSizes: std::ptr::null_mut(),
MarkerLineColor: auto_col,
MarkerLineColors: std::ptr::null_mut(),
MarkerFillColor: auto_col,
MarkerFillColors: std::ptr::null_mut(),
Offset: 0,
Stride: IMPLOT3D_AUTO,
Flags: sys::ImPlot3DItemFlags_None as _,
}
}
fn plot3d_spec_from(flags: u32, offset: i32, stride: i32) -> sys::ImPlot3DSpec_c {
let mut spec = take_next_plot3d_spec().unwrap_or_else(default_plot3d_spec);
spec.Flags = ((spec.Flags as u32) | flags) as sys::ImPlot3DItemFlags;
spec.Offset = offset;
spec.Stride = stride;
spec
}
trait ImVec2Ctor {
fn from_xy(x: f32, y: f32) -> Self;
}
impl ImVec2Ctor for sys::ImVec2_c {
fn from_xy(x: f32, y: f32) -> Self {
Self { x, y }
}
}
impl ImVec2Ctor for imgui_sys::ImVec2_c {
fn from_xy(x: f32, y: f32) -> Self {
Self { x, y }
}
}
#[inline]
fn imvec2<T: ImVec2Ctor>(x: f32, y: f32) -> T {
T::from_xy(x, y)
}
trait ImVec4Ctor {
fn from_xyzw(x: f32, y: f32, z: f32, w: f32) -> Self;
}
impl ImVec4Ctor for sys::ImVec4_c {
fn from_xyzw(x: f32, y: f32, z: f32, w: f32) -> Self {
Self { x, y, z, w }
}
}
impl ImVec4Ctor for imgui_sys::ImVec4_c {
fn from_xyzw(x: f32, y: f32, z: f32, w: f32) -> Self {
Self { x, y, z, w }
}
}
#[inline]
fn imvec4<T: ImVec4Ctor>(x: f32, y: f32, z: f32, w: f32) -> T {
T::from_xyzw(x, y, z, w)
}
#[allow(non_snake_case)]
mod compat_ffi {
use super::{imgui_sys, sys};
unsafe extern "C" {
pub fn ImPlot3D_PlotToPixels_double(x: f64, y: f64, z: f64) -> imgui_sys::ImVec2_c;
pub fn ImPlot3D_GetPlotRectPos() -> imgui_sys::ImVec2_c;
pub fn ImPlot3D_GetPlotRectSize() -> imgui_sys::ImVec2_c;
pub fn ImPlot3D_NextColormapColor() -> imgui_sys::ImVec4_c;
pub fn ImPlot3D_GetColormapColor(
idx: ::std::os::raw::c_int,
cmap: sys::ImPlot3DColormap,
) -> imgui_sys::ImVec4_c;
}
}
#[cfg(debug_assertions)]
thread_local! {
static DEBUG_PLOT_STATE: PlotDebugState = PlotDebugState { in_plot: std::cell::Cell::new(false), setup_locked: std::cell::Cell::new(false) };
}
#[cfg(debug_assertions)]
struct PlotDebugState {
in_plot: std::cell::Cell<bool>,
setup_locked: std::cell::Cell<bool>,
}
#[cfg(debug_assertions)]
#[inline]
fn debug_begin_plot() {
DEBUG_PLOT_STATE.with(|s| {
s.in_plot.set(true);
s.setup_locked.set(false);
});
}
#[cfg(debug_assertions)]
#[inline]
fn debug_end_plot() {
DEBUG_PLOT_STATE.with(|s| {
s.in_plot.set(false);
s.setup_locked.set(false);
});
}
#[cfg(debug_assertions)]
#[inline]
fn debug_before_setup() {
DEBUG_PLOT_STATE.with(|s| {
debug_assert!(
s.in_plot.get(),
"Setup* called outside of BeginPlot/EndPlot"
);
debug_assert!(
!s.setup_locked.get(),
"Setup* must be called before any plotting (PlotX) or locking operations"
);
});
}
#[cfg(debug_assertions)]
#[inline]
fn debug_before_plot() {
DEBUG_PLOT_STATE.with(|s| {
debug_assert!(s.in_plot.get(), "Plot* called outside of BeginPlot/EndPlot");
s.setup_locked.set(true);
});
}
#[cfg(not(debug_assertions))]
#[inline]
fn debug_begin_plot() {}
#[cfg(not(debug_assertions))]
#[inline]
fn debug_end_plot() {}
#[cfg(not(debug_assertions))]
#[inline]
fn debug_before_setup() {}
#[cfg(not(debug_assertions))]
#[inline]
fn debug_before_plot() {}
pub fn show_all_demos() {
unsafe { sys::ImPlot3D_ShowAllDemos() }
}
pub fn show_demo_window() {
unsafe { sys::ImPlot3D_ShowDemoWindow(std::ptr::null_mut()) }
}
pub fn show_demo_window_with_flag(p_open: &mut bool) {
unsafe { sys::ImPlot3D_ShowDemoWindow(p_open as *mut bool) }
}
pub fn show_style_editor() {
unsafe { sys::ImPlot3D_ShowStyleEditor(std::ptr::null_mut()) }
}
pub fn show_metrics_window() {
unsafe { sys::ImPlot3D_ShowMetricsWindow(std::ptr::null_mut()) }
}
pub fn show_metrics_window_with_flag(p_open: &mut bool) {
unsafe { sys::ImPlot3D_ShowMetricsWindow(p_open as *mut bool) }
}
pub struct Plot3DContext {
raw: *mut sys::ImPlot3DContext,
imgui_ctx_raw: *mut imgui_sys::ImGuiContext,
imgui_alive: dear_imgui_rs::ContextAliveToken,
}
impl Plot3DContext {
pub fn try_create(imgui: &Context) -> dear_imgui_rs::ImGuiResult<Self> {
let imgui_ctx_raw = imgui.as_raw();
let imgui_alive = imgui.alive_token();
assert_eq!(
unsafe { imgui_sys::igGetCurrentContext() },
imgui_ctx_raw,
"dear-implot3d: Plot3DContext must be created with the currently-active ImGui context"
);
unsafe {
let ctx = sys::ImPlot3D_CreateContext();
if ctx.is_null() {
return Err(dear_imgui_rs::ImGuiError::context_creation(
"ImPlot3D_CreateContext returned null",
));
}
sys::ImPlot3D_SetCurrentContext(ctx);
Ok(Self {
raw: ctx,
imgui_ctx_raw,
imgui_alive,
})
}
}
pub fn create(imgui: &Context) -> Self {
Self::try_create(imgui).expect("Failed to create ImPlot3D context")
}
pub fn set_as_current(&self) {
unsafe {
sys::ImPlot3D_SetCurrentContext(self.raw);
}
}
pub fn raw_style_mut() -> *mut sys::ImPlot3DStyle {
unsafe { sys::ImPlot3D_GetStyle() }
}
pub fn get_plot_ui<'ui>(&self, ui: &'ui Ui) -> Plot3DUi<'ui> {
Plot3DUi { _ui: ui }
}
}
impl Drop for Plot3DContext {
fn drop(&mut self) {
if self.raw.is_null() {
return;
}
if !self.imgui_alive.is_alive() {
return;
}
unsafe {
let prev_imgui = imgui_sys::igGetCurrentContext();
imgui_sys::igSetCurrentContext(self.imgui_ctx_raw);
if sys::ImPlot3D_GetCurrentContext() == self.raw {
sys::ImPlot3D_SetCurrentContext(std::ptr::null_mut());
}
sys::ImPlot3D_DestroyContext(self.raw);
imgui_sys::igSetCurrentContext(prev_imgui);
}
}
}
pub struct Plot3DUi<'ui> {
_ui: &'ui Ui,
}
pub struct Plot3DToken;
impl<'ui> Plot3DUi<'ui> {
pub fn begin_plot<S: AsRef<str>>(&self, title: S) -> Plot3DBuilder {
Plot3DBuilder {
title: title.as_ref().into(),
size: None,
flags: Plot3DFlags::empty(),
}
}
pub fn plot_line_f32<S: AsRef<str>>(
&self,
label: S,
xs: &[f32],
ys: &[f32],
zs: &[f32],
flags: Line3DFlags,
) {
if xs.len() != ys.len() || ys.len() != zs.len() {
return;
}
let Some(count) = len_i32(xs.len()) else {
return;
};
let label = label.as_ref();
if label.contains('\0') {
return;
}
let stride_bytes = std::mem::size_of::<f32>() as i32;
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
let spec = plot3d_spec_from(flags.bits(), 0, stride_bytes);
sys::ImPlot3D_PlotLine_FloatPtr(
label_ptr,
xs.as_ptr(),
ys.as_ptr(),
zs.as_ptr(),
count,
spec,
);
})
}
pub fn plot_line_f32_raw<S: AsRef<str>>(
&self,
label: S,
xs: &[f32],
ys: &[f32],
zs: &[f32],
flags: Line3DFlags,
offset: i32,
stride: i32,
) {
if xs.len() != ys.len() || ys.len() != zs.len() {
return;
}
let Some(count) = len_i32(xs.len()) else {
return;
};
let label = label.as_ref();
if label.contains('\0') {
return;
}
let stride_bytes = if stride == 0 {
std::mem::size_of::<f32>() as i32
} else {
stride
};
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
let spec = plot3d_spec_from(flags.bits(), offset, stride_bytes);
sys::ImPlot3D_PlotLine_FloatPtr(
label_ptr,
xs.as_ptr(),
ys.as_ptr(),
zs.as_ptr(),
count,
spec,
);
})
}
pub fn plot_line_f64<S: AsRef<str>>(
&self,
label: S,
xs: &[f64],
ys: &[f64],
zs: &[f64],
flags: Line3DFlags,
) {
if xs.len() != ys.len() || ys.len() != zs.len() {
return;
}
let Some(count) = len_i32(xs.len()) else {
return;
};
let label = label.as_ref();
if label.contains('\0') {
return;
}
let stride_bytes = std::mem::size_of::<f64>() as i32;
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
let spec = plot3d_spec_from(flags.bits(), 0, stride_bytes);
sys::ImPlot3D_PlotLine_doublePtr(
label_ptr,
xs.as_ptr(),
ys.as_ptr(),
zs.as_ptr(),
count,
spec,
);
})
}
pub fn plot_line_f64_raw<S: AsRef<str>>(
&self,
label: S,
xs: &[f64],
ys: &[f64],
zs: &[f64],
flags: Line3DFlags,
offset: i32,
stride: i32,
) {
if xs.len() != ys.len() || ys.len() != zs.len() {
return;
}
let Some(count) = len_i32(xs.len()) else {
return;
};
let label = label.as_ref();
if label.contains('\0') {
return;
}
let stride_bytes = if stride == 0 {
std::mem::size_of::<f64>() as i32
} else {
stride
};
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
let spec = plot3d_spec_from(flags.bits(), offset, stride_bytes);
sys::ImPlot3D_PlotLine_doublePtr(
label_ptr,
xs.as_ptr(),
ys.as_ptr(),
zs.as_ptr(),
count,
spec,
);
})
}
pub fn plot_scatter_f32<S: AsRef<str>>(
&self,
label: S,
xs: &[f32],
ys: &[f32],
zs: &[f32],
flags: Scatter3DFlags,
) {
if xs.len() != ys.len() || ys.len() != zs.len() {
return;
}
let Some(count) = len_i32(xs.len()) else {
return;
};
let label = label.as_ref();
if label.contains('\0') {
return;
}
let stride_bytes = std::mem::size_of::<f32>() as i32;
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
let spec = plot3d_spec_from(flags.bits(), 0, stride_bytes);
sys::ImPlot3D_PlotScatter_FloatPtr(
label_ptr,
xs.as_ptr(),
ys.as_ptr(),
zs.as_ptr(),
count,
spec,
);
})
}
pub fn plot_scatter_f32_raw<S: AsRef<str>>(
&self,
label: S,
xs: &[f32],
ys: &[f32],
zs: &[f32],
flags: Scatter3DFlags,
offset: i32,
stride: i32,
) {
if xs.len() != ys.len() || ys.len() != zs.len() {
return;
}
let Some(count) = len_i32(xs.len()) else {
return;
};
let label = label.as_ref();
if label.contains('\0') {
return;
}
let stride_bytes = if stride == 0 {
std::mem::size_of::<f32>() as i32
} else {
stride
};
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
let spec = plot3d_spec_from(flags.bits(), offset, stride_bytes);
sys::ImPlot3D_PlotScatter_FloatPtr(
label_ptr,
xs.as_ptr(),
ys.as_ptr(),
zs.as_ptr(),
count,
spec,
);
})
}
pub fn plot_scatter_f64<S: AsRef<str>>(
&self,
label: S,
xs: &[f64],
ys: &[f64],
zs: &[f64],
flags: Scatter3DFlags,
) {
if xs.len() != ys.len() || ys.len() != zs.len() {
return;
}
let Some(count) = len_i32(xs.len()) else {
return;
};
let label = label.as_ref();
if label.contains('\0') {
return;
}
let stride_bytes = std::mem::size_of::<f64>() as i32;
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
let spec = plot3d_spec_from(flags.bits(), 0, stride_bytes);
sys::ImPlot3D_PlotScatter_doublePtr(
label_ptr,
xs.as_ptr(),
ys.as_ptr(),
zs.as_ptr(),
count,
spec,
);
})
}
pub fn plot_scatter_f64_raw<S: AsRef<str>>(
&self,
label: S,
xs: &[f64],
ys: &[f64],
zs: &[f64],
flags: Scatter3DFlags,
offset: i32,
stride: i32,
) {
if xs.len() != ys.len() || ys.len() != zs.len() {
return;
}
let Some(count) = len_i32(xs.len()) else {
return;
};
let label = label.as_ref();
if label.contains('\0') {
return;
}
let stride_bytes = if stride == 0 {
std::mem::size_of::<f64>() as i32
} else {
stride
};
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
let spec = plot3d_spec_from(flags.bits(), offset, stride_bytes);
sys::ImPlot3D_PlotScatter_doublePtr(
label_ptr,
xs.as_ptr(),
ys.as_ptr(),
zs.as_ptr(),
count,
spec,
);
})
}
pub fn plot_triangles_f32<S: AsRef<str>>(
&self,
label: S,
xs: &[f32],
ys: &[f32],
zs: &[f32],
flags: Triangle3DFlags,
) {
if xs.len() != ys.len() || ys.len() != zs.len() {
return;
}
let Some(count) = len_i32(xs.len()) else {
return;
};
let label = label.as_ref();
if label.contains('\0') {
return;
}
let stride_bytes = std::mem::size_of::<f32>() as i32;
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
let spec = plot3d_spec_from(flags.bits(), 0, stride_bytes);
sys::ImPlot3D_PlotTriangle_FloatPtr(
label_ptr,
xs.as_ptr(),
ys.as_ptr(),
zs.as_ptr(),
count,
spec,
);
})
}
pub fn plot_triangles_f32_raw<S: AsRef<str>>(
&self,
label: S,
xs: &[f32],
ys: &[f32],
zs: &[f32],
flags: Triangle3DFlags,
offset: i32,
stride: i32,
) {
if xs.len() != ys.len() || ys.len() != zs.len() {
return;
}
let Some(count) = len_i32(xs.len()) else {
return;
};
let label = label.as_ref();
if label.contains('\0') {
return;
}
let stride_bytes = if stride == 0 {
std::mem::size_of::<f32>() as i32
} else {
stride
};
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
let spec = plot3d_spec_from(flags.bits(), offset, stride_bytes);
sys::ImPlot3D_PlotTriangle_FloatPtr(
label_ptr,
xs.as_ptr(),
ys.as_ptr(),
zs.as_ptr(),
count,
spec,
);
})
}
pub fn plot_quads_f32<S: AsRef<str>>(
&self,
label: S,
xs: &[f32],
ys: &[f32],
zs: &[f32],
flags: Quad3DFlags,
) {
if xs.len() != ys.len() || ys.len() != zs.len() {
return;
}
let Some(count) = len_i32(xs.len()) else {
return;
};
let label = label.as_ref();
if label.contains('\0') {
return;
}
let stride_bytes = std::mem::size_of::<f32>() as i32;
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
let spec = plot3d_spec_from(flags.bits(), 0, stride_bytes);
sys::ImPlot3D_PlotQuad_FloatPtr(
label_ptr,
xs.as_ptr(),
ys.as_ptr(),
zs.as_ptr(),
count,
spec,
);
})
}
pub fn plot_quads_f32_raw<S: AsRef<str>>(
&self,
label: S,
xs: &[f32],
ys: &[f32],
zs: &[f32],
flags: Quad3DFlags,
offset: i32,
stride: i32,
) {
if xs.len() != ys.len() || ys.len() != zs.len() {
return;
}
let Some(count) = len_i32(xs.len()) else {
return;
};
let label = label.as_ref();
if label.contains('\0') {
return;
}
let stride_bytes = if stride == 0 {
std::mem::size_of::<f32>() as i32
} else {
stride
};
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
let spec = plot3d_spec_from(flags.bits(), offset, stride_bytes);
sys::ImPlot3D_PlotQuad_FloatPtr(
label_ptr,
xs.as_ptr(),
ys.as_ptr(),
zs.as_ptr(),
count,
spec,
);
})
}
pub fn plot_triangles_f64<S: AsRef<str>>(
&self,
label: S,
xs: &[f64],
ys: &[f64],
zs: &[f64],
flags: Triangle3DFlags,
) {
if xs.len() != ys.len() || ys.len() != zs.len() {
return;
}
let Some(count) = len_i32(xs.len()) else {
return;
};
let label = label.as_ref();
if label.contains('\0') {
return;
}
let stride_bytes = std::mem::size_of::<f64>() as i32;
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
let spec = plot3d_spec_from(flags.bits(), 0, stride_bytes);
sys::ImPlot3D_PlotTriangle_doublePtr(
label_ptr,
xs.as_ptr(),
ys.as_ptr(),
zs.as_ptr(),
count,
spec,
);
})
}
pub fn plot_triangles_f64_raw<S: AsRef<str>>(
&self,
label: S,
xs: &[f64],
ys: &[f64],
zs: &[f64],
flags: Triangle3DFlags,
offset: i32,
stride: i32,
) {
if xs.len() != ys.len() || ys.len() != zs.len() {
return;
}
let Some(count) = len_i32(xs.len()) else {
return;
};
let label = label.as_ref();
if label.contains('\0') {
return;
}
let stride_bytes = if stride == 0 {
std::mem::size_of::<f64>() as i32
} else {
stride
};
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
let spec = plot3d_spec_from(flags.bits(), offset, stride_bytes);
sys::ImPlot3D_PlotTriangle_doublePtr(
label_ptr,
xs.as_ptr(),
ys.as_ptr(),
zs.as_ptr(),
count,
spec,
);
})
}
pub fn plot_quads_f64<S: AsRef<str>>(
&self,
label: S,
xs: &[f64],
ys: &[f64],
zs: &[f64],
flags: Quad3DFlags,
) {
if xs.len() != ys.len() || ys.len() != zs.len() {
return;
}
let Some(count) = len_i32(xs.len()) else {
return;
};
let label = label.as_ref();
if label.contains('\0') {
return;
}
let stride_bytes = std::mem::size_of::<f64>() as i32;
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
let spec = plot3d_spec_from(flags.bits(), 0, stride_bytes);
sys::ImPlot3D_PlotQuad_doublePtr(
label_ptr,
xs.as_ptr(),
ys.as_ptr(),
zs.as_ptr(),
count,
spec,
);
})
}
pub fn plot_quads_f64_raw<S: AsRef<str>>(
&self,
label: S,
xs: &[f64],
ys: &[f64],
zs: &[f64],
flags: Quad3DFlags,
offset: i32,
stride: i32,
) {
if xs.len() != ys.len() || ys.len() != zs.len() {
return;
}
let Some(count) = len_i32(xs.len()) else {
return;
};
let label = label.as_ref();
if label.contains('\0') {
return;
}
let stride_bytes = if stride == 0 {
std::mem::size_of::<f64>() as i32
} else {
stride
};
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
let spec = plot3d_spec_from(flags.bits(), offset, stride_bytes);
sys::ImPlot3D_PlotQuad_doublePtr(
label_ptr,
xs.as_ptr(),
ys.as_ptr(),
zs.as_ptr(),
count,
spec,
);
})
}
}
impl Drop for Plot3DToken {
fn drop(&mut self) {
unsafe {
debug_end_plot();
sys::ImPlot3D_EndPlot();
}
}
}
pub struct Plot3DBuilder {
title: String,
size: Option<[f32; 2]>,
flags: Plot3DFlags,
}
impl Plot3DBuilder {
pub fn size(mut self, size: [f32; 2]) -> Self {
self.size = Some(size);
self
}
pub fn flags(mut self, flags: Plot3DFlags) -> Self {
self.flags = flags;
self
}
pub fn build(self) -> Option<Plot3DToken> {
if self.title.contains('\0') {
return None;
}
let title = self.title;
let size = self.size.unwrap_or([0.0, 0.0]);
let ok = dear_imgui_rs::with_scratch_txt(&title, |title_ptr| unsafe {
let style = sys::ImPlot3D_GetStyle();
if !style.is_null() {
let count = sys::ImPlot3D_GetColormapCount();
if count > 0 && ((*style).Colormap < 0 || (*style).Colormap >= count) {
(*style).Colormap = 0;
}
}
sys::ImPlot3D_BeginPlot(
title_ptr,
imvec2(size[0], size[1]),
self.flags.bits() as i32,
)
});
if ok {
debug_begin_plot();
Some(Plot3DToken)
} else {
None
}
}
}
#[cfg(feature = "mint")]
impl<'ui> Plot3DUi<'ui> {
pub fn plot_line_mint<S: AsRef<str>>(
&self,
label: S,
pts: &[mint::Point3<f32>],
flags: Line3DFlags,
) {
let mut xs = Vec::with_capacity(pts.len());
let mut ys = Vec::with_capacity(pts.len());
let mut zs = Vec::with_capacity(pts.len());
for p in pts {
xs.push(p.x);
ys.push(p.y);
zs.push(p.z);
}
self.plot_line_f32(label, &xs, &ys, &zs, flags);
}
pub fn plot_scatter_mint<S: AsRef<str>>(
&self,
label: S,
pts: &[mint::Point3<f32>],
flags: Scatter3DFlags,
) {
let mut xs = Vec::with_capacity(pts.len());
let mut ys = Vec::with_capacity(pts.len());
let mut zs = Vec::with_capacity(pts.len());
for p in pts {
xs.push(p.x);
ys.push(p.y);
zs.push(p.z);
}
self.plot_scatter_f32(label, &xs, &ys, &zs, flags);
}
pub fn plot_text_mint(
&self,
text: &str,
pos: mint::Point3<f32>,
angle: f32,
pix_offset: [f32; 2],
) {
self.plot_text(text, pos.x, pos.y, pos.z, angle, pix_offset);
}
pub fn plot_to_pixels_mint(&self, point: mint::Point3<f32>) -> [f32; 2] {
self.plot_to_pixels([point.x, point.y, point.z])
}
}
pub struct Surface3DBuilder<'ui> {
_ui: &'ui Plot3DUi<'ui>,
label: Cow<'ui, str>,
xs: &'ui [f32],
ys: &'ui [f32],
zs: &'ui [f32],
scale_min: f64,
scale_max: f64,
flags: Surface3DFlags,
item_flags: Item3DFlags,
style: Plot3DItemStyle,
}
impl<'ui> Surface3DBuilder<'ui> {
pub fn scale(mut self, min: f64, max: f64) -> Self {
self.scale_min = min;
self.scale_max = max;
self
}
pub fn flags(mut self, flags: Surface3DFlags) -> Self {
self.flags = flags;
self
}
pub fn plot(self) {
let x_count = match i32::try_from(self.xs.len()) {
Ok(v) => v,
Err(_) => return,
};
let y_count = match i32::try_from(self.ys.len()) {
Ok(v) => v,
Err(_) => return,
};
let expected = match self.xs.len().checked_mul(self.ys.len()) {
Some(v) => v,
None => return,
};
if self.zs.len() != expected {
return;
}
let label = self.label.as_ref();
let label = if label.contains('\0') {
"surface"
} else {
label
};
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
let spec = plot3d_spec_with_style(
self.style,
self.flags.bits() | self.item_flags.bits(),
0,
std::mem::size_of::<f32>() as i32,
);
sys::ImPlot3D_PlotSurface_FloatPtr(
label_ptr,
self.xs.as_ptr(),
self.ys.as_ptr(),
self.zs.as_ptr(),
x_count,
y_count,
self.scale_min,
self.scale_max,
spec,
);
})
}
}
impl<'ui> Plot3DUi<'ui> {
pub fn surface_f32(
&'ui self,
label: impl Into<Cow<'ui, str>>,
xs: &'ui [f32],
ys: &'ui [f32],
zs: &'ui [f32],
) -> Surface3DBuilder<'ui> {
Surface3DBuilder {
_ui: self,
label: label.into(),
xs,
ys,
zs,
scale_min: f64::NAN,
scale_max: f64::NAN,
flags: Surface3DFlags::NONE,
item_flags: Item3DFlags::NONE,
style: Plot3DItemStyle::default(),
}
}
pub fn surface_f32_raw<S: AsRef<str>>(
&self,
label: S,
xs: &[f32],
ys: &[f32],
zs: &[f32],
scale_min: f64,
scale_max: f64,
flags: Surface3DFlags,
offset: i32,
stride: i32,
) {
debug_before_plot();
let x_count = xs.len();
let y_count = ys.len();
let expected = match x_count.checked_mul(y_count) {
Some(v) => v,
None => return,
};
if zs.len() != expected {
return;
}
let mut xs_flat = Vec::with_capacity(expected);
let mut ys_flat = Vec::with_capacity(expected);
for yi in 0..y_count {
for xi in 0..x_count {
xs_flat.push(xs[xi]);
ys_flat.push(ys[yi]);
}
}
let label = label.as_ref();
if label.contains('\0') {
return;
}
let stride_bytes = if stride == 0 {
std::mem::size_of::<f32>() as i32
} else {
stride
};
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
let spec = plot3d_spec_from(flags.bits(), offset, stride_bytes);
sys::ImPlot3D_PlotSurface_FloatPtr(
label_ptr,
xs_flat.as_ptr(),
ys_flat.as_ptr(),
zs.as_ptr(),
x_count as i32,
y_count as i32,
scale_min,
scale_max,
spec,
);
})
}
pub fn surface_f32_flat<S: AsRef<str>>(
&self,
label: S,
xs_flat: &[f32],
ys_flat: &[f32],
zs: &[f32],
x_count: i32,
y_count: i32,
scale_min: f64,
scale_max: f64,
flags: Surface3DFlags,
offset: i32,
stride: i32,
) {
debug_before_plot();
if x_count <= 0 || y_count <= 0 {
return;
}
let expected = (x_count as usize).saturating_mul(y_count as usize);
if xs_flat.len() != expected || ys_flat.len() != expected || zs.len() != expected {
return;
}
let label = label.as_ref();
if label.contains('\0') {
return;
}
let stride_bytes = if stride == 0 {
std::mem::size_of::<f32>() as i32
} else {
stride
};
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
let spec = plot3d_spec_from(flags.bits(), offset, stride_bytes);
sys::ImPlot3D_PlotSurface_FloatPtr(
label_ptr,
xs_flat.as_ptr(),
ys_flat.as_ptr(),
zs.as_ptr(),
x_count,
y_count,
scale_min,
scale_max,
spec,
);
})
}
}
pub struct Image3DByAxesBuilder<'ui> {
_ui: &'ui Plot3DUi<'ui>,
label: Cow<'ui, str>,
tex_ref: sys::ImTextureRef_c,
center: [f32; 3],
axis_u: [f32; 3],
axis_v: [f32; 3],
uv0: [f32; 2],
uv1: [f32; 2],
tint: [f32; 4],
flags: Image3DFlags,
item_flags: Item3DFlags,
style: Plot3DItemStyle,
}
impl<'ui> Image3DByAxesBuilder<'ui> {
pub fn uv(mut self, uv0: [f32; 2], uv1: [f32; 2]) -> Self {
self.uv0 = uv0;
self.uv1 = uv1;
self
}
pub fn tint(mut self, col: [f32; 4]) -> Self {
self.tint = col;
self
}
pub fn flags(mut self, flags: Image3DFlags) -> Self {
self.flags = flags;
self
}
pub fn plot(self) {
let label = self.label.as_ref();
let label = if label.contains('\0') { "image" } else { label };
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
debug_before_plot();
let spec = plot3d_spec_with_style(
self.style,
self.flags.bits() | self.item_flags.bits(),
0,
IMPLOT3D_AUTO,
);
sys::ImPlot3D_PlotImage_Vec2(
label_ptr,
self.tex_ref,
sys::ImPlot3DPoint_c {
x: self.center[0] as f64,
y: self.center[1] as f64,
z: self.center[2] as f64,
},
sys::ImPlot3DPoint_c {
x: self.axis_u[0] as f64,
y: self.axis_u[1] as f64,
z: self.axis_u[2] as f64,
},
sys::ImPlot3DPoint_c {
x: self.axis_v[0] as f64,
y: self.axis_v[1] as f64,
z: self.axis_v[2] as f64,
},
imvec2(self.uv0[0], self.uv0[1]),
imvec2(self.uv1[0], self.uv1[1]),
imvec4(self.tint[0], self.tint[1], self.tint[2], self.tint[3]),
spec,
);
})
}
}
pub struct Image3DByCornersBuilder<'ui> {
_ui: &'ui Plot3DUi<'ui>,
label: Cow<'ui, str>,
tex_ref: sys::ImTextureRef_c,
p0: [f32; 3],
p1: [f32; 3],
p2: [f32; 3],
p3: [f32; 3],
uv0: [f32; 2],
uv1: [f32; 2],
uv2: [f32; 2],
uv3: [f32; 2],
tint: [f32; 4],
flags: Image3DFlags,
item_flags: Item3DFlags,
style: Plot3DItemStyle,
}
impl<'ui> Image3DByCornersBuilder<'ui> {
pub fn uvs(mut self, uv0: [f32; 2], uv1: [f32; 2], uv2: [f32; 2], uv3: [f32; 2]) -> Self {
self.uv0 = uv0;
self.uv1 = uv1;
self.uv2 = uv2;
self.uv3 = uv3;
self
}
pub fn tint(mut self, col: [f32; 4]) -> Self {
self.tint = col;
self
}
pub fn flags(mut self, flags: Image3DFlags) -> Self {
self.flags = flags;
self
}
pub fn plot(self) {
let label = self.label.as_ref();
let label = if label.contains('\0') { "image" } else { label };
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
debug_before_plot();
let spec = plot3d_spec_with_style(
self.style,
self.flags.bits() | self.item_flags.bits(),
0,
IMPLOT3D_AUTO,
);
sys::ImPlot3D_PlotImage_Plot3DPoint(
label_ptr,
self.tex_ref,
sys::ImPlot3DPoint_c {
x: self.p0[0] as f64,
y: self.p0[1] as f64,
z: self.p0[2] as f64,
},
sys::ImPlot3DPoint_c {
x: self.p1[0] as f64,
y: self.p1[1] as f64,
z: self.p1[2] as f64,
},
sys::ImPlot3DPoint_c {
x: self.p2[0] as f64,
y: self.p2[1] as f64,
z: self.p2[2] as f64,
},
sys::ImPlot3DPoint_c {
x: self.p3[0] as f64,
y: self.p3[1] as f64,
z: self.p3[2] as f64,
},
imvec2(self.uv0[0], self.uv0[1]),
imvec2(self.uv1[0], self.uv1[1]),
imvec2(self.uv2[0], self.uv2[1]),
imvec2(self.uv3[0], self.uv3[1]),
imvec4(self.tint[0], self.tint[1], self.tint[2], self.tint[3]),
spec,
);
})
}
}
impl<'ui> Plot3DUi<'ui> {
pub fn image_by_axes<T: Into<TextureRef>>(
&'ui self,
label: impl Into<Cow<'ui, str>>,
tex: T,
center: [f32; 3],
axis_u: [f32; 3],
axis_v: [f32; 3],
) -> Image3DByAxesBuilder<'ui> {
let tr = tex.into().raw();
let tex_ref = sys::ImTextureRef_c {
_TexData: tr._TexData as *mut sys::ImTextureData,
_TexID: tr._TexID as sys::ImTextureID,
};
debug_before_plot();
Image3DByAxesBuilder {
_ui: self,
label: label.into(),
tex_ref,
center,
axis_u,
axis_v,
uv0: [0.0, 0.0],
uv1: [1.0, 1.0],
tint: [1.0, 1.0, 1.0, 1.0],
flags: Image3DFlags::NONE,
item_flags: Item3DFlags::NONE,
style: Plot3DItemStyle::default(),
}
}
pub fn image_by_corners<T: Into<TextureRef>>(
&'ui self,
label: impl Into<Cow<'ui, str>>,
tex: T,
p0: [f32; 3],
p1: [f32; 3],
p2: [f32; 3],
p3: [f32; 3],
) -> Image3DByCornersBuilder<'ui> {
let tr = tex.into().raw();
let tex_ref = sys::ImTextureRef_c {
_TexData: tr._TexData as *mut sys::ImTextureData,
_TexID: tr._TexID as sys::ImTextureID,
};
debug_before_plot();
Image3DByCornersBuilder {
_ui: self,
label: label.into(),
tex_ref,
p0,
p1,
p2,
p3,
uv0: [0.0, 0.0],
uv1: [1.0, 0.0],
uv2: [1.0, 1.0],
uv3: [0.0, 1.0],
tint: [1.0, 1.0, 1.0, 1.0],
flags: Image3DFlags::NONE,
item_flags: Item3DFlags::NONE,
style: Plot3DItemStyle::default(),
}
}
}
impl<'ui> Plot3DUi<'ui> {
pub fn setup_axes(
&self,
x_label: &str,
y_label: &str,
z_label: &str,
x_flags: Axis3DFlags,
y_flags: Axis3DFlags,
z_flags: Axis3DFlags,
) {
debug_before_setup();
if x_label.contains('\0') || y_label.contains('\0') || z_label.contains('\0') {
return;
}
dear_imgui_rs::with_scratch_txt_three(
x_label,
y_label,
z_label,
|x_ptr, y_ptr, z_ptr| unsafe {
sys::ImPlot3D_SetupAxes(
x_ptr,
y_ptr,
z_ptr,
x_flags.bits() as i32,
y_flags.bits() as i32,
z_flags.bits() as i32,
)
},
)
}
pub fn setup_axis(&self, axis: Axis3D, label: &str, flags: Axis3DFlags) {
debug_before_setup();
if label.contains('\0') {
return;
}
dear_imgui_rs::with_scratch_txt(label, |ptr| unsafe {
sys::ImPlot3D_SetupAxis(axis as i32, ptr, flags.bits() as i32)
})
}
pub fn setup_axis_limits(&self, axis: Axis3D, min: f64, max: f64, cond: Plot3DCond) {
debug_before_setup();
unsafe { sys::ImPlot3D_SetupAxisLimits(axis as i32, min, max, cond as i32) }
}
pub fn setup_axes_limits(
&self,
x_min: f64,
x_max: f64,
y_min: f64,
y_max: f64,
z_min: f64,
z_max: f64,
cond: Plot3DCond,
) {
debug_before_setup();
unsafe {
sys::ImPlot3D_SetupAxesLimits(x_min, x_max, y_min, y_max, z_min, z_max, cond as i32)
}
}
pub fn setup_axis_limits_constraints(&self, axis: Axis3D, v_min: f64, v_max: f64) {
debug_before_setup();
unsafe { sys::ImPlot3D_SetupAxisLimitsConstraints(axis as i32, v_min, v_max) }
}
pub fn setup_axis_zoom_constraints(&self, axis: Axis3D, z_min: f64, z_max: f64) {
debug_before_setup();
unsafe { sys::ImPlot3D_SetupAxisZoomConstraints(axis as i32, z_min, z_max) }
}
pub fn setup_axis_ticks_values(
&self,
axis: Axis3D,
values: &[f64],
labels: Option<&[&str]>,
keep_default: bool,
) {
debug_before_setup();
let Some(n_ticks) = len_i32(values.len()) else {
return;
};
if let Some(lbls) = labels {
if lbls.len() != values.len() {
return;
}
let cleaned: Vec<&str> = lbls
.iter()
.map(|&s| if s.contains('\0') { "" } else { s })
.collect();
dear_imgui_rs::with_scratch_txt_slice(&cleaned, |ptrs| unsafe {
sys::ImPlot3D_SetupAxisTicks_doublePtr(
axis as i32,
values.as_ptr(),
n_ticks,
ptrs.as_ptr(),
keep_default,
)
});
} else {
unsafe {
sys::ImPlot3D_SetupAxisTicks_doublePtr(
axis as i32,
values.as_ptr(),
n_ticks,
std::ptr::null(),
keep_default,
)
};
}
}
pub fn setup_axis_ticks_range(
&self,
axis: Axis3D,
v_min: f64,
v_max: f64,
n_ticks: i32,
labels: Option<&[&str]>,
keep_default: bool,
) {
debug_before_setup();
if let Some(lbls) = labels {
let cleaned: Vec<&str> = lbls
.iter()
.map(|&s| if s.contains('\0') { "" } else { s })
.collect();
dear_imgui_rs::with_scratch_txt_slice(&cleaned, |ptrs| unsafe {
sys::ImPlot3D_SetupAxisTicks_double(
axis as i32,
v_min,
v_max,
n_ticks,
ptrs.as_ptr(),
keep_default,
)
});
} else {
unsafe {
sys::ImPlot3D_SetupAxisTicks_double(
axis as i32,
v_min,
v_max,
n_ticks,
std::ptr::null(),
keep_default,
)
};
}
}
pub fn setup_box_scale(&self, x: f32, y: f32, z: f32) {
debug_before_setup();
unsafe { sys::ImPlot3D_SetupBoxScale(x as f64, y as f64, z as f64) }
}
pub fn setup_box_rotation(
&self,
elevation: f32,
azimuth: f32,
animate: bool,
cond: Plot3DCond,
) {
debug_before_setup();
unsafe {
sys::ImPlot3D_SetupBoxRotation_double(
elevation as f64,
azimuth as f64,
animate,
cond as i32,
)
}
}
pub fn setup_box_initial_rotation(&self, elevation: f32, azimuth: f32) {
debug_before_setup();
unsafe { sys::ImPlot3D_SetupBoxInitialRotation_double(elevation as f64, azimuth as f64) }
}
pub fn plot_text(&self, text: &str, x: f32, y: f32, z: f32, angle: f32, pix_offset: [f32; 2]) {
if text.contains('\0') {
return;
}
dear_imgui_rs::with_scratch_txt(text, |text_ptr| unsafe {
debug_before_plot();
sys::ImPlot3D_PlotText(
text_ptr,
x as f64,
y as f64,
z as f64,
angle as f64,
imvec2(pix_offset[0], pix_offset[1]),
)
})
}
pub fn plot_to_pixels(&self, point: [f32; 3]) -> [f32; 2] {
unsafe {
let out = compat_ffi::ImPlot3D_PlotToPixels_double(
point[0] as f64,
point[1] as f64,
point[2] as f64,
);
[out.x, out.y]
}
}
pub fn get_plot_draw_list(&self) -> *mut sys::ImDrawList {
unsafe { sys::ImPlot3D_GetPlotDrawList() }
}
pub fn get_frame_pos(&self) -> [f32; 2] {
unsafe {
let out = compat_ffi::ImPlot3D_GetPlotRectPos();
[out.x, out.y]
}
}
pub fn get_frame_size(&self) -> [f32; 2] {
unsafe {
let out = compat_ffi::ImPlot3D_GetPlotRectSize();
[out.x, out.y]
}
}
}
pub struct Mesh3DBuilder<'ui> {
_ui: &'ui Plot3DUi<'ui>,
label: Cow<'ui, str>,
vertices: &'ui [[f32; 3]],
indices: &'ui [u32],
flags: Mesh3DFlags,
item_flags: Item3DFlags,
style: Plot3DItemStyle,
}
impl<'ui> Mesh3DBuilder<'ui> {
pub fn flags(mut self, flags: Mesh3DFlags) -> Self {
self.flags = flags;
self
}
pub fn plot(self) {
let Some(vtx_count) = len_i32(self.vertices.len()) else {
return;
};
let Some(idx_count) = len_i32(self.indices.len()) else {
return;
};
let mut xs = Vec::with_capacity(self.vertices.len());
let mut ys = Vec::with_capacity(self.vertices.len());
let mut zs = Vec::with_capacity(self.vertices.len());
for [x, y, z] in self.vertices.iter().copied() {
xs.push(x);
ys.push(y);
zs.push(z);
}
let label = self.label.as_ref();
let label = if label.contains('\0') { "mesh" } else { label };
dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
debug_before_plot();
let spec = plot3d_spec_with_style(
self.style,
self.flags.bits() | self.item_flags.bits(),
0,
std::mem::size_of::<f32>() as i32,
);
sys::ImPlot3D_PlotMesh_FloatPtr(
label_ptr,
xs.as_ptr(),
ys.as_ptr(),
zs.as_ptr(),
self.indices.as_ptr(),
vtx_count,
idx_count,
spec,
);
})
}
}
impl<'ui> Plot3DUi<'ui> {
pub fn mesh(
&'ui self,
label: impl Into<Cow<'ui, str>>,
vertices: &'ui [[f32; 3]],
indices: &'ui [u32],
) -> Mesh3DBuilder<'ui> {
Mesh3DBuilder {
_ui: self,
label: label.into(),
vertices,
indices,
flags: Mesh3DFlags::NONE,
item_flags: Item3DFlags::NONE,
style: Plot3DItemStyle::default(),
}
}
}
#[cfg(test)]
mod tests {
use super::sys;
use std::mem::{align_of, size_of};
#[test]
fn ffi_layout_implot3d_point_is_3_f64() {
assert_eq!(size_of::<sys::ImPlot3DPoint>(), 3 * size_of::<f64>());
assert_eq!(align_of::<sys::ImPlot3DPoint>(), align_of::<f64>());
}
}