1use dear_imgui_rs::sys as imgui_sys;
42use dear_imgui_rs::texture::TextureRef;
43pub use dear_imgui_rs::{Context, Ui};
44use dear_implot3d_sys as sys;
45
46mod flags;
47mod item_style;
48mod style;
49mod ui_ext;
50
51pub use flags::*;
52pub use item_style::*;
53pub use style::*;
54pub use ui_ext::*;
55pub mod meshes;
56pub mod plots;
57pub use plots::*;
58
59use std::borrow::Cow;
60use std::cell::RefCell;
61
62fn len_i32(len: usize) -> Option<i32> {
63 i32::try_from(len).ok()
64}
65
66const IMPLOT3D_AUTO: i32 = -1;
67
68thread_local! {
69 static NEXT_PLOT3D_SPEC: RefCell<Option<sys::ImPlot3DSpec_c>> = RefCell::new(None);
70}
71
72pub(crate) fn update_next_plot3d_spec(f: impl FnOnce(&mut sys::ImPlot3DSpec_c)) {
73 NEXT_PLOT3D_SPEC.with(|cell| {
74 let mut guard = cell.borrow_mut();
75 let mut spec = guard.take().unwrap_or_else(default_plot3d_spec);
76 f(&mut spec);
77 *guard = Some(spec);
78 })
79}
80
81fn take_next_plot3d_spec() -> Option<sys::ImPlot3DSpec_c> {
82 NEXT_PLOT3D_SPEC.with(|cell| cell.borrow_mut().take())
83}
84
85fn set_next_plot3d_spec(spec: Option<sys::ImPlot3DSpec_c>) {
86 NEXT_PLOT3D_SPEC.with(|cell| {
87 *cell.borrow_mut() = spec;
88 })
89}
90
91fn default_plot3d_spec() -> sys::ImPlot3DSpec_c {
92 let auto_col = sys::ImVec4_c {
93 x: 0.0,
94 y: 0.0,
95 z: 0.0,
96 w: -1.0,
97 };
98
99 sys::ImPlot3DSpec_c {
100 LineColor: auto_col,
101 LineColors: std::ptr::null_mut(),
102 LineWeight: 1.0,
103 FillColor: auto_col,
104 FillColors: std::ptr::null_mut(),
105 FillAlpha: -1.0,
106 Marker: sys::ImPlot3DMarker_Auto as _,
107 MarkerSize: -1.0,
108 MarkerSizes: std::ptr::null_mut(),
109 MarkerLineColor: auto_col,
110 MarkerLineColors: std::ptr::null_mut(),
111 MarkerFillColor: auto_col,
112 MarkerFillColors: std::ptr::null_mut(),
113 Offset: 0,
114 Stride: IMPLOT3D_AUTO,
115 Flags: sys::ImPlot3DItemFlags_None as _,
116 }
117}
118
119fn plot3d_spec_from(flags: u32, offset: i32, stride: i32) -> sys::ImPlot3DSpec_c {
120 let mut spec = take_next_plot3d_spec().unwrap_or_else(default_plot3d_spec);
121 spec.Flags = ((spec.Flags as u32) | flags) as sys::ImPlot3DItemFlags;
122 spec.Offset = offset;
123 spec.Stride = stride;
124 spec
125}
126
127trait ImVec2Ctor {
128 fn from_xy(x: f32, y: f32) -> Self;
129}
130
131impl ImVec2Ctor for sys::ImVec2_c {
132 fn from_xy(x: f32, y: f32) -> Self {
133 Self { x, y }
134 }
135}
136
137impl ImVec2Ctor for imgui_sys::ImVec2_c {
138 fn from_xy(x: f32, y: f32) -> Self {
139 Self { x, y }
140 }
141}
142
143#[inline]
144fn imvec2<T: ImVec2Ctor>(x: f32, y: f32) -> T {
145 T::from_xy(x, y)
146}
147
148trait ImVec4Ctor {
149 fn from_xyzw(x: f32, y: f32, z: f32, w: f32) -> Self;
150}
151
152impl ImVec4Ctor for sys::ImVec4_c {
153 fn from_xyzw(x: f32, y: f32, z: f32, w: f32) -> Self {
154 Self { x, y, z, w }
155 }
156}
157
158impl ImVec4Ctor for imgui_sys::ImVec4_c {
159 fn from_xyzw(x: f32, y: f32, z: f32, w: f32) -> Self {
160 Self { x, y, z, w }
161 }
162}
163
164#[inline]
165fn imvec4<T: ImVec4Ctor>(x: f32, y: f32, z: f32, w: f32) -> T {
166 T::from_xyzw(x, y, z, w)
167}
168
169#[allow(non_snake_case)]
170mod compat_ffi {
171 use super::{imgui_sys, sys};
172
173 unsafe extern "C" {
174 pub fn ImPlot3D_PlotToPixels_double(x: f64, y: f64, z: f64) -> imgui_sys::ImVec2_c;
175 pub fn ImPlot3D_GetPlotRectPos() -> imgui_sys::ImVec2_c;
176 pub fn ImPlot3D_GetPlotRectSize() -> imgui_sys::ImVec2_c;
177 pub fn ImPlot3D_NextColormapColor() -> imgui_sys::ImVec4_c;
178 pub fn ImPlot3D_GetColormapColor(
179 idx: ::std::os::raw::c_int,
180 cmap: sys::ImPlot3DColormap,
181 ) -> imgui_sys::ImVec4_c;
182 }
183}
184
185#[cfg(debug_assertions)]
187thread_local! {
188 static DEBUG_PLOT_STATE: PlotDebugState = PlotDebugState { in_plot: std::cell::Cell::new(false), setup_locked: std::cell::Cell::new(false) };
189}
190
191#[cfg(debug_assertions)]
192struct PlotDebugState {
193 in_plot: std::cell::Cell<bool>,
194 setup_locked: std::cell::Cell<bool>,
195}
196
197#[cfg(debug_assertions)]
198#[inline]
199fn debug_begin_plot() {
200 DEBUG_PLOT_STATE.with(|s| {
201 s.in_plot.set(true);
202 s.setup_locked.set(false);
203 });
204}
205
206#[cfg(debug_assertions)]
207#[inline]
208fn debug_end_plot() {
209 DEBUG_PLOT_STATE.with(|s| {
210 s.in_plot.set(false);
211 s.setup_locked.set(false);
212 });
213}
214
215#[cfg(debug_assertions)]
216#[inline]
217fn debug_before_setup() {
218 DEBUG_PLOT_STATE.with(|s| {
219 debug_assert!(
220 s.in_plot.get(),
221 "Setup* called outside of BeginPlot/EndPlot"
222 );
223 debug_assert!(
224 !s.setup_locked.get(),
225 "Setup* must be called before any plotting (PlotX) or locking operations"
226 );
227 });
228}
229
230#[cfg(debug_assertions)]
231#[inline]
232fn debug_before_plot() {
233 DEBUG_PLOT_STATE.with(|s| {
234 debug_assert!(s.in_plot.get(), "Plot* called outside of BeginPlot/EndPlot");
235 s.setup_locked.set(true);
236 });
237}
238
239#[cfg(not(debug_assertions))]
240#[inline]
241fn debug_begin_plot() {}
242#[cfg(not(debug_assertions))]
243#[inline]
244fn debug_end_plot() {}
245#[cfg(not(debug_assertions))]
246#[inline]
247fn debug_before_setup() {}
248#[cfg(not(debug_assertions))]
249#[inline]
250fn debug_before_plot() {}
251
252pub fn show_all_demos() {
257 unsafe { sys::ImPlot3D_ShowAllDemos() }
258}
259
260pub fn show_demo_window() {
274 unsafe { sys::ImPlot3D_ShowDemoWindow(std::ptr::null_mut()) }
275}
276
277pub fn show_demo_window_with_flag(p_open: &mut bool) {
279 unsafe { sys::ImPlot3D_ShowDemoWindow(p_open as *mut bool) }
280}
281
282pub fn show_style_editor() {
287 unsafe { sys::ImPlot3D_ShowStyleEditor(std::ptr::null_mut()) }
288}
289
290pub fn show_metrics_window() {
295 unsafe { sys::ImPlot3D_ShowMetricsWindow(std::ptr::null_mut()) }
296}
297
298pub fn show_metrics_window_with_flag(p_open: &mut bool) {
300 unsafe { sys::ImPlot3D_ShowMetricsWindow(p_open as *mut bool) }
301}
302
303pub struct Plot3DContext {
322 raw: *mut sys::ImPlot3DContext,
323 imgui_ctx_raw: *mut imgui_sys::ImGuiContext,
324 imgui_alive: dear_imgui_rs::ContextAliveToken,
325}
326
327impl Plot3DContext {
328 pub fn try_create(imgui: &Context) -> dear_imgui_rs::ImGuiResult<Self> {
332 let imgui_ctx_raw = imgui.as_raw();
333 let imgui_alive = imgui.alive_token();
334 assert_eq!(
335 unsafe { imgui_sys::igGetCurrentContext() },
336 imgui_ctx_raw,
337 "dear-implot3d: Plot3DContext must be created with the currently-active ImGui context"
338 );
339 unsafe {
340 let ctx = sys::ImPlot3D_CreateContext();
341 if ctx.is_null() {
342 return Err(dear_imgui_rs::ImGuiError::context_creation(
343 "ImPlot3D_CreateContext returned null",
344 ));
345 }
346
347 sys::ImPlot3D_SetCurrentContext(ctx);
349 Ok(Self {
350 raw: ctx,
351 imgui_ctx_raw,
352 imgui_alive,
353 })
354 }
355 }
356
357 pub fn create(imgui: &Context) -> Self {
359 Self::try_create(imgui).expect("Failed to create ImPlot3D context")
360 }
361
362 pub fn set_as_current(&self) {
364 unsafe {
365 sys::ImPlot3D_SetCurrentContext(self.raw);
366 }
367 }
368
369 pub fn raw_style_mut() -> *mut sys::ImPlot3DStyle {
374 unsafe { sys::ImPlot3D_GetStyle() }
375 }
376
377 pub fn get_plot_ui<'ui>(&self, ui: &'ui Ui) -> Plot3DUi<'ui> {
382 Plot3DUi { _ui: ui }
383 }
384}
385
386impl Drop for Plot3DContext {
387 fn drop(&mut self) {
388 if self.raw.is_null() {
389 return;
390 }
391
392 if !self.imgui_alive.is_alive() {
393 return;
396 }
397
398 unsafe {
399 let prev_imgui = imgui_sys::igGetCurrentContext();
400 imgui_sys::igSetCurrentContext(self.imgui_ctx_raw);
401
402 if sys::ImPlot3D_GetCurrentContext() == self.raw {
403 sys::ImPlot3D_SetCurrentContext(std::ptr::null_mut());
404 }
405 sys::ImPlot3D_DestroyContext(self.raw);
406
407 imgui_sys::igSetCurrentContext(prev_imgui);
408 }
409 }
410}
411
412pub struct Plot3DUi<'ui> {
433 _ui: &'ui Ui,
434}
435
436pub struct Plot3DToken;
441
442impl<'ui> Plot3DUi<'ui> {
443 pub fn begin_plot<S: AsRef<str>>(&self, title: S) -> Plot3DBuilder {
463 Plot3DBuilder {
464 title: title.as_ref().into(),
465 size: None,
466 flags: Plot3DFlags::empty(),
467 }
468 }
469
470 pub fn plot_line_f32<S: AsRef<str>>(
495 &self,
496 label: S,
497 xs: &[f32],
498 ys: &[f32],
499 zs: &[f32],
500 flags: Line3DFlags,
501 ) {
502 if xs.len() != ys.len() || ys.len() != zs.len() {
503 return;
504 }
505 let Some(count) = len_i32(xs.len()) else {
506 return;
507 };
508 let label = label.as_ref();
509 if label.contains('\0') {
510 return;
511 }
512 let stride_bytes = std::mem::size_of::<f32>() as i32;
513 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
514 let spec = plot3d_spec_from(flags.bits(), 0, stride_bytes);
515 sys::ImPlot3D_PlotLine_FloatPtr(
516 label_ptr,
517 xs.as_ptr(),
518 ys.as_ptr(),
519 zs.as_ptr(),
520 count,
521 spec,
522 );
523 })
524 }
525
526 pub fn plot_line_f32_raw<S: AsRef<str>>(
528 &self,
529 label: S,
530 xs: &[f32],
531 ys: &[f32],
532 zs: &[f32],
533 flags: Line3DFlags,
534 offset: i32,
535 stride: i32,
536 ) {
537 if xs.len() != ys.len() || ys.len() != zs.len() {
538 return;
539 }
540 let Some(count) = len_i32(xs.len()) else {
541 return;
542 };
543 let label = label.as_ref();
544 if label.contains('\0') {
545 return;
546 }
547 let stride_bytes = if stride == 0 {
548 std::mem::size_of::<f32>() as i32
549 } else {
550 stride
551 };
552 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
553 let spec = plot3d_spec_from(flags.bits(), offset, stride_bytes);
554 sys::ImPlot3D_PlotLine_FloatPtr(
555 label_ptr,
556 xs.as_ptr(),
557 ys.as_ptr(),
558 zs.as_ptr(),
559 count,
560 spec,
561 );
562 })
563 }
564
565 pub fn plot_line_f64<S: AsRef<str>>(
567 &self,
568 label: S,
569 xs: &[f64],
570 ys: &[f64],
571 zs: &[f64],
572 flags: Line3DFlags,
573 ) {
574 if xs.len() != ys.len() || ys.len() != zs.len() {
575 return;
576 }
577 let Some(count) = len_i32(xs.len()) else {
578 return;
579 };
580 let label = label.as_ref();
581 if label.contains('\0') {
582 return;
583 }
584 let stride_bytes = std::mem::size_of::<f64>() as i32;
585 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
586 let spec = plot3d_spec_from(flags.bits(), 0, stride_bytes);
587 sys::ImPlot3D_PlotLine_doublePtr(
588 label_ptr,
589 xs.as_ptr(),
590 ys.as_ptr(),
591 zs.as_ptr(),
592 count,
593 spec,
594 );
595 })
596 }
597
598 pub fn plot_line_f64_raw<S: AsRef<str>>(
600 &self,
601 label: S,
602 xs: &[f64],
603 ys: &[f64],
604 zs: &[f64],
605 flags: Line3DFlags,
606 offset: i32,
607 stride: i32,
608 ) {
609 if xs.len() != ys.len() || ys.len() != zs.len() {
610 return;
611 }
612 let Some(count) = len_i32(xs.len()) else {
613 return;
614 };
615 let label = label.as_ref();
616 if label.contains('\0') {
617 return;
618 }
619 let stride_bytes = if stride == 0 {
620 std::mem::size_of::<f64>() as i32
621 } else {
622 stride
623 };
624 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
625 let spec = plot3d_spec_from(flags.bits(), offset, stride_bytes);
626 sys::ImPlot3D_PlotLine_doublePtr(
627 label_ptr,
628 xs.as_ptr(),
629 ys.as_ptr(),
630 zs.as_ptr(),
631 count,
632 spec,
633 );
634 })
635 }
636
637 pub fn plot_scatter_f32<S: AsRef<str>>(
639 &self,
640 label: S,
641 xs: &[f32],
642 ys: &[f32],
643 zs: &[f32],
644 flags: Scatter3DFlags,
645 ) {
646 if xs.len() != ys.len() || ys.len() != zs.len() {
647 return;
648 }
649 let Some(count) = len_i32(xs.len()) else {
650 return;
651 };
652 let label = label.as_ref();
653 if label.contains('\0') {
654 return;
655 }
656 let stride_bytes = std::mem::size_of::<f32>() as i32;
657 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
658 let spec = plot3d_spec_from(flags.bits(), 0, stride_bytes);
659 sys::ImPlot3D_PlotScatter_FloatPtr(
660 label_ptr,
661 xs.as_ptr(),
662 ys.as_ptr(),
663 zs.as_ptr(),
664 count,
665 spec,
666 );
667 })
668 }
669
670 pub fn plot_scatter_f32_raw<S: AsRef<str>>(
672 &self,
673 label: S,
674 xs: &[f32],
675 ys: &[f32],
676 zs: &[f32],
677 flags: Scatter3DFlags,
678 offset: i32,
679 stride: i32,
680 ) {
681 if xs.len() != ys.len() || ys.len() != zs.len() {
682 return;
683 }
684 let Some(count) = len_i32(xs.len()) else {
685 return;
686 };
687 let label = label.as_ref();
688 if label.contains('\0') {
689 return;
690 }
691 let stride_bytes = if stride == 0 {
692 std::mem::size_of::<f32>() as i32
693 } else {
694 stride
695 };
696 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
697 let spec = plot3d_spec_from(flags.bits(), offset, stride_bytes);
698 sys::ImPlot3D_PlotScatter_FloatPtr(
699 label_ptr,
700 xs.as_ptr(),
701 ys.as_ptr(),
702 zs.as_ptr(),
703 count,
704 spec,
705 );
706 })
707 }
708
709 pub fn plot_scatter_f64<S: AsRef<str>>(
711 &self,
712 label: S,
713 xs: &[f64],
714 ys: &[f64],
715 zs: &[f64],
716 flags: Scatter3DFlags,
717 ) {
718 if xs.len() != ys.len() || ys.len() != zs.len() {
719 return;
720 }
721 let Some(count) = len_i32(xs.len()) else {
722 return;
723 };
724 let label = label.as_ref();
725 if label.contains('\0') {
726 return;
727 }
728 let stride_bytes = std::mem::size_of::<f64>() as i32;
729 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
730 let spec = plot3d_spec_from(flags.bits(), 0, stride_bytes);
731 sys::ImPlot3D_PlotScatter_doublePtr(
732 label_ptr,
733 xs.as_ptr(),
734 ys.as_ptr(),
735 zs.as_ptr(),
736 count,
737 spec,
738 );
739 })
740 }
741
742 pub fn plot_scatter_f64_raw<S: AsRef<str>>(
744 &self,
745 label: S,
746 xs: &[f64],
747 ys: &[f64],
748 zs: &[f64],
749 flags: Scatter3DFlags,
750 offset: i32,
751 stride: i32,
752 ) {
753 if xs.len() != ys.len() || ys.len() != zs.len() {
754 return;
755 }
756 let Some(count) = len_i32(xs.len()) else {
757 return;
758 };
759 let label = label.as_ref();
760 if label.contains('\0') {
761 return;
762 }
763 let stride_bytes = if stride == 0 {
764 std::mem::size_of::<f64>() as i32
765 } else {
766 stride
767 };
768 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
769 let spec = plot3d_spec_from(flags.bits(), offset, stride_bytes);
770 sys::ImPlot3D_PlotScatter_doublePtr(
771 label_ptr,
772 xs.as_ptr(),
773 ys.as_ptr(),
774 zs.as_ptr(),
775 count,
776 spec,
777 );
778 })
779 }
780
781 pub fn plot_triangles_f32<S: AsRef<str>>(
783 &self,
784 label: S,
785 xs: &[f32],
786 ys: &[f32],
787 zs: &[f32],
788 flags: Triangle3DFlags,
789 ) {
790 if xs.len() != ys.len() || ys.len() != zs.len() {
791 return;
792 }
793 let Some(count) = len_i32(xs.len()) else {
794 return;
795 };
796 let label = label.as_ref();
797 if label.contains('\0') {
798 return;
799 }
800 let stride_bytes = std::mem::size_of::<f32>() as i32;
801 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
802 let spec = plot3d_spec_from(flags.bits(), 0, stride_bytes);
803 sys::ImPlot3D_PlotTriangle_FloatPtr(
804 label_ptr,
805 xs.as_ptr(),
806 ys.as_ptr(),
807 zs.as_ptr(),
808 count,
809 spec,
810 );
811 })
812 }
813
814 pub fn plot_triangles_f32_raw<S: AsRef<str>>(
815 &self,
816 label: S,
817 xs: &[f32],
818 ys: &[f32],
819 zs: &[f32],
820 flags: Triangle3DFlags,
821 offset: i32,
822 stride: i32,
823 ) {
824 if xs.len() != ys.len() || ys.len() != zs.len() {
825 return;
826 }
827 let Some(count) = len_i32(xs.len()) else {
828 return;
829 };
830 let label = label.as_ref();
831 if label.contains('\0') {
832 return;
833 }
834 let stride_bytes = if stride == 0 {
835 std::mem::size_of::<f32>() as i32
836 } else {
837 stride
838 };
839 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
840 let spec = plot3d_spec_from(flags.bits(), offset, stride_bytes);
841 sys::ImPlot3D_PlotTriangle_FloatPtr(
842 label_ptr,
843 xs.as_ptr(),
844 ys.as_ptr(),
845 zs.as_ptr(),
846 count,
847 spec,
848 );
849 })
850 }
851
852 pub fn plot_quads_f32<S: AsRef<str>>(
854 &self,
855 label: S,
856 xs: &[f32],
857 ys: &[f32],
858 zs: &[f32],
859 flags: Quad3DFlags,
860 ) {
861 if xs.len() != ys.len() || ys.len() != zs.len() {
862 return;
863 }
864 let Some(count) = len_i32(xs.len()) else {
865 return;
866 };
867 let label = label.as_ref();
868 if label.contains('\0') {
869 return;
870 }
871 let stride_bytes = std::mem::size_of::<f32>() as i32;
872 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
873 let spec = plot3d_spec_from(flags.bits(), 0, stride_bytes);
874 sys::ImPlot3D_PlotQuad_FloatPtr(
875 label_ptr,
876 xs.as_ptr(),
877 ys.as_ptr(),
878 zs.as_ptr(),
879 count,
880 spec,
881 );
882 })
883 }
884
885 pub fn plot_quads_f32_raw<S: AsRef<str>>(
886 &self,
887 label: S,
888 xs: &[f32],
889 ys: &[f32],
890 zs: &[f32],
891 flags: Quad3DFlags,
892 offset: i32,
893 stride: i32,
894 ) {
895 if xs.len() != ys.len() || ys.len() != zs.len() {
896 return;
897 }
898 let Some(count) = len_i32(xs.len()) else {
899 return;
900 };
901 let label = label.as_ref();
902 if label.contains('\0') {
903 return;
904 }
905 let stride_bytes = if stride == 0 {
906 std::mem::size_of::<f32>() as i32
907 } else {
908 stride
909 };
910 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
911 let spec = plot3d_spec_from(flags.bits(), offset, stride_bytes);
912 sys::ImPlot3D_PlotQuad_FloatPtr(
913 label_ptr,
914 xs.as_ptr(),
915 ys.as_ptr(),
916 zs.as_ptr(),
917 count,
918 spec,
919 );
920 })
921 }
922
923 pub fn plot_triangles_f64<S: AsRef<str>>(
925 &self,
926 label: S,
927 xs: &[f64],
928 ys: &[f64],
929 zs: &[f64],
930 flags: Triangle3DFlags,
931 ) {
932 if xs.len() != ys.len() || ys.len() != zs.len() {
933 return;
934 }
935 let Some(count) = len_i32(xs.len()) else {
936 return;
937 };
938 let label = label.as_ref();
939 if label.contains('\0') {
940 return;
941 }
942 let stride_bytes = std::mem::size_of::<f64>() as i32;
943 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
944 let spec = plot3d_spec_from(flags.bits(), 0, stride_bytes);
945 sys::ImPlot3D_PlotTriangle_doublePtr(
946 label_ptr,
947 xs.as_ptr(),
948 ys.as_ptr(),
949 zs.as_ptr(),
950 count,
951 spec,
952 );
953 })
954 }
955
956 pub fn plot_triangles_f64_raw<S: AsRef<str>>(
957 &self,
958 label: S,
959 xs: &[f64],
960 ys: &[f64],
961 zs: &[f64],
962 flags: Triangle3DFlags,
963 offset: i32,
964 stride: i32,
965 ) {
966 if xs.len() != ys.len() || ys.len() != zs.len() {
967 return;
968 }
969 let Some(count) = len_i32(xs.len()) else {
970 return;
971 };
972 let label = label.as_ref();
973 if label.contains('\0') {
974 return;
975 }
976 let stride_bytes = if stride == 0 {
977 std::mem::size_of::<f64>() as i32
978 } else {
979 stride
980 };
981 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
982 let spec = plot3d_spec_from(flags.bits(), offset, stride_bytes);
983 sys::ImPlot3D_PlotTriangle_doublePtr(
984 label_ptr,
985 xs.as_ptr(),
986 ys.as_ptr(),
987 zs.as_ptr(),
988 count,
989 spec,
990 );
991 })
992 }
993
994 pub fn plot_quads_f64<S: AsRef<str>>(
996 &self,
997 label: S,
998 xs: &[f64],
999 ys: &[f64],
1000 zs: &[f64],
1001 flags: Quad3DFlags,
1002 ) {
1003 if xs.len() != ys.len() || ys.len() != zs.len() {
1004 return;
1005 }
1006 let Some(count) = len_i32(xs.len()) else {
1007 return;
1008 };
1009 let label = label.as_ref();
1010 if label.contains('\0') {
1011 return;
1012 }
1013 let stride_bytes = std::mem::size_of::<f64>() as i32;
1014 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
1015 let spec = plot3d_spec_from(flags.bits(), 0, stride_bytes);
1016 sys::ImPlot3D_PlotQuad_doublePtr(
1017 label_ptr,
1018 xs.as_ptr(),
1019 ys.as_ptr(),
1020 zs.as_ptr(),
1021 count,
1022 spec,
1023 );
1024 })
1025 }
1026
1027 pub fn plot_quads_f64_raw<S: AsRef<str>>(
1028 &self,
1029 label: S,
1030 xs: &[f64],
1031 ys: &[f64],
1032 zs: &[f64],
1033 flags: Quad3DFlags,
1034 offset: i32,
1035 stride: i32,
1036 ) {
1037 if xs.len() != ys.len() || ys.len() != zs.len() {
1038 return;
1039 }
1040 let Some(count) = len_i32(xs.len()) else {
1041 return;
1042 };
1043 let label = label.as_ref();
1044 if label.contains('\0') {
1045 return;
1046 }
1047 let stride_bytes = if stride == 0 {
1048 std::mem::size_of::<f64>() as i32
1049 } else {
1050 stride
1051 };
1052 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
1053 let spec = plot3d_spec_from(flags.bits(), offset, stride_bytes);
1054 sys::ImPlot3D_PlotQuad_doublePtr(
1055 label_ptr,
1056 xs.as_ptr(),
1057 ys.as_ptr(),
1058 zs.as_ptr(),
1059 count,
1060 spec,
1061 );
1062 })
1063 }
1064}
1065
1066impl Drop for Plot3DToken {
1067 fn drop(&mut self) {
1068 unsafe {
1069 debug_end_plot();
1070 sys::ImPlot3D_EndPlot();
1071 }
1072 }
1073}
1074
1075pub struct Plot3DBuilder {
1077 title: String,
1078 size: Option<[f32; 2]>,
1079 flags: Plot3DFlags,
1080}
1081
1082impl Plot3DBuilder {
1083 pub fn size(mut self, size: [f32; 2]) -> Self {
1084 self.size = Some(size);
1085 self
1086 }
1087 pub fn flags(mut self, flags: Plot3DFlags) -> Self {
1088 self.flags = flags;
1089 self
1090 }
1091 pub fn build(self) -> Option<Plot3DToken> {
1092 if self.title.contains('\0') {
1093 return None;
1094 }
1095 let title = self.title;
1096 let size = self.size.unwrap_or([0.0, 0.0]);
1097 let ok = dear_imgui_rs::with_scratch_txt(&title, |title_ptr| unsafe {
1098 let style = sys::ImPlot3D_GetStyle();
1100 if !style.is_null() {
1101 let count = sys::ImPlot3D_GetColormapCount();
1102 if count > 0 && ((*style).Colormap < 0 || (*style).Colormap >= count) {
1103 (*style).Colormap = 0;
1104 }
1105 }
1106 sys::ImPlot3D_BeginPlot(
1107 title_ptr,
1108 imvec2(size[0], size[1]),
1109 self.flags.bits() as i32,
1110 )
1111 });
1112 if ok {
1113 debug_begin_plot();
1114 Some(Plot3DToken)
1115 } else {
1116 None
1117 }
1118 }
1119}
1120
1121#[cfg(feature = "mint")]
1148impl<'ui> Plot3DUi<'ui> {
1149 pub fn plot_line_mint<S: AsRef<str>>(
1153 &self,
1154 label: S,
1155 pts: &[mint::Point3<f32>],
1156 flags: Line3DFlags,
1157 ) {
1158 let mut xs = Vec::with_capacity(pts.len());
1159 let mut ys = Vec::with_capacity(pts.len());
1160 let mut zs = Vec::with_capacity(pts.len());
1161 for p in pts {
1162 xs.push(p.x);
1163 ys.push(p.y);
1164 zs.push(p.z);
1165 }
1166 self.plot_line_f32(label, &xs, &ys, &zs, flags);
1167 }
1168
1169 pub fn plot_scatter_mint<S: AsRef<str>>(
1171 &self,
1172 label: S,
1173 pts: &[mint::Point3<f32>],
1174 flags: Scatter3DFlags,
1175 ) {
1176 let mut xs = Vec::with_capacity(pts.len());
1177 let mut ys = Vec::with_capacity(pts.len());
1178 let mut zs = Vec::with_capacity(pts.len());
1179 for p in pts {
1180 xs.push(p.x);
1181 ys.push(p.y);
1182 zs.push(p.z);
1183 }
1184 self.plot_scatter_f32(label, &xs, &ys, &zs, flags);
1185 }
1186
1187 pub fn plot_text_mint(
1189 &self,
1190 text: &str,
1191 pos: mint::Point3<f32>,
1192 angle: f32,
1193 pix_offset: [f32; 2],
1194 ) {
1195 self.plot_text(text, pos.x, pos.y, pos.z, angle, pix_offset);
1196 }
1197
1198 pub fn plot_to_pixels_mint(&self, point: mint::Point3<f32>) -> [f32; 2] {
1200 self.plot_to_pixels([point.x, point.y, point.z])
1201 }
1202}
1203
1204pub struct Surface3DBuilder<'ui> {
1206 _ui: &'ui Plot3DUi<'ui>,
1207 label: Cow<'ui, str>,
1208 xs: &'ui [f32],
1209 ys: &'ui [f32],
1210 zs: &'ui [f32],
1211 scale_min: f64,
1212 scale_max: f64,
1213 flags: Surface3DFlags,
1214 item_flags: Item3DFlags,
1215 style: Plot3DItemStyle,
1216}
1217
1218impl<'ui> Surface3DBuilder<'ui> {
1219 pub fn scale(mut self, min: f64, max: f64) -> Self {
1220 self.scale_min = min;
1221 self.scale_max = max;
1222 self
1223 }
1224 pub fn flags(mut self, flags: Surface3DFlags) -> Self {
1225 self.flags = flags;
1226 self
1227 }
1228 pub fn plot(self) {
1229 let x_count = match i32::try_from(self.xs.len()) {
1230 Ok(v) => v,
1231 Err(_) => return,
1232 };
1233 let y_count = match i32::try_from(self.ys.len()) {
1234 Ok(v) => v,
1235 Err(_) => return,
1236 };
1237 let expected = match self.xs.len().checked_mul(self.ys.len()) {
1238 Some(v) => v,
1239 None => return,
1240 };
1241 if self.zs.len() != expected {
1242 return;
1243 }
1244 let label = self.label.as_ref();
1245 let label = if label.contains('\0') {
1246 "surface"
1247 } else {
1248 label
1249 };
1250 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
1251 let spec = plot3d_spec_with_style(
1252 self.style,
1253 self.flags.bits() | self.item_flags.bits(),
1254 0,
1255 std::mem::size_of::<f32>() as i32,
1256 );
1257 sys::ImPlot3D_PlotSurface_FloatPtr(
1258 label_ptr,
1259 self.xs.as_ptr(),
1260 self.ys.as_ptr(),
1261 self.zs.as_ptr(),
1262 x_count,
1263 y_count,
1264 self.scale_min,
1265 self.scale_max,
1266 spec,
1267 );
1268 })
1269 }
1270}
1271
1272impl<'ui> Plot3DUi<'ui> {
1273 pub fn surface_f32(
1275 &'ui self,
1276 label: impl Into<Cow<'ui, str>>,
1277 xs: &'ui [f32],
1278 ys: &'ui [f32],
1279 zs: &'ui [f32],
1280 ) -> Surface3DBuilder<'ui> {
1281 Surface3DBuilder {
1282 _ui: self,
1283 label: label.into(),
1284 xs,
1285 ys,
1286 zs,
1287 scale_min: f64::NAN,
1288 scale_max: f64::NAN,
1289 flags: Surface3DFlags::NONE,
1290 item_flags: Item3DFlags::NONE,
1291 style: Plot3DItemStyle::default(),
1292 }
1293 }
1294
1295 pub fn surface_f32_raw<S: AsRef<str>>(
1297 &self,
1298 label: S,
1299 xs: &[f32],
1300 ys: &[f32],
1301 zs: &[f32],
1302 scale_min: f64,
1303 scale_max: f64,
1304 flags: Surface3DFlags,
1305 offset: i32,
1306 stride: i32,
1307 ) {
1308 debug_before_plot();
1309 let x_count = xs.len();
1310 let y_count = ys.len();
1311 let expected = match x_count.checked_mul(y_count) {
1312 Some(v) => v,
1313 None => return,
1314 };
1315 if zs.len() != expected {
1316 return;
1318 }
1319
1320 let mut xs_flat = Vec::with_capacity(expected);
1322 let mut ys_flat = Vec::with_capacity(expected);
1323 for yi in 0..y_count {
1324 for xi in 0..x_count {
1325 xs_flat.push(xs[xi]);
1326 ys_flat.push(ys[yi]);
1327 }
1328 }
1329
1330 let label = label.as_ref();
1331 if label.contains('\0') {
1332 return;
1333 }
1334 let stride_bytes = if stride == 0 {
1335 std::mem::size_of::<f32>() as i32
1336 } else {
1337 stride
1338 };
1339 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
1340 let spec = plot3d_spec_from(flags.bits(), offset, stride_bytes);
1341 sys::ImPlot3D_PlotSurface_FloatPtr(
1342 label_ptr,
1343 xs_flat.as_ptr(),
1344 ys_flat.as_ptr(),
1345 zs.as_ptr(),
1346 x_count as i32,
1347 y_count as i32,
1348 scale_min,
1349 scale_max,
1350 spec,
1351 );
1352 })
1353 }
1354
1355 pub fn surface_f32_flat<S: AsRef<str>>(
1360 &self,
1361 label: S,
1362 xs_flat: &[f32],
1363 ys_flat: &[f32],
1364 zs: &[f32],
1365 x_count: i32,
1366 y_count: i32,
1367 scale_min: f64,
1368 scale_max: f64,
1369 flags: Surface3DFlags,
1370 offset: i32,
1371 stride: i32,
1372 ) {
1373 debug_before_plot();
1374 if x_count <= 0 || y_count <= 0 {
1375 return;
1376 }
1377 let expected = (x_count as usize).saturating_mul(y_count as usize);
1378 if xs_flat.len() != expected || ys_flat.len() != expected || zs.len() != expected {
1379 return;
1380 }
1381 let label = label.as_ref();
1382 if label.contains('\0') {
1383 return;
1384 }
1385 let stride_bytes = if stride == 0 {
1386 std::mem::size_of::<f32>() as i32
1387 } else {
1388 stride
1389 };
1390 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
1391 let spec = plot3d_spec_from(flags.bits(), offset, stride_bytes);
1392 sys::ImPlot3D_PlotSurface_FloatPtr(
1393 label_ptr,
1394 xs_flat.as_ptr(),
1395 ys_flat.as_ptr(),
1396 zs.as_ptr(),
1397 x_count,
1398 y_count,
1399 scale_min,
1400 scale_max,
1401 spec,
1402 );
1403 })
1404 }
1405}
1406
1407pub struct Image3DByAxesBuilder<'ui> {
1409 _ui: &'ui Plot3DUi<'ui>,
1410 label: Cow<'ui, str>,
1411 tex_ref: sys::ImTextureRef_c,
1412 center: [f32; 3],
1413 axis_u: [f32; 3],
1414 axis_v: [f32; 3],
1415 uv0: [f32; 2],
1416 uv1: [f32; 2],
1417 tint: [f32; 4],
1418 flags: Image3DFlags,
1419 item_flags: Item3DFlags,
1420 style: Plot3DItemStyle,
1421}
1422
1423impl<'ui> Image3DByAxesBuilder<'ui> {
1424 pub fn uv(mut self, uv0: [f32; 2], uv1: [f32; 2]) -> Self {
1425 self.uv0 = uv0;
1426 self.uv1 = uv1;
1427 self
1428 }
1429 pub fn tint(mut self, col: [f32; 4]) -> Self {
1430 self.tint = col;
1431 self
1432 }
1433 pub fn flags(mut self, flags: Image3DFlags) -> Self {
1434 self.flags = flags;
1435 self
1436 }
1437 pub fn plot(self) {
1438 let label = self.label.as_ref();
1439 let label = if label.contains('\0') { "image" } else { label };
1440 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
1441 debug_before_plot();
1442 let spec = plot3d_spec_with_style(
1443 self.style,
1444 self.flags.bits() | self.item_flags.bits(),
1445 0,
1446 IMPLOT3D_AUTO,
1447 );
1448 sys::ImPlot3D_PlotImage_Vec2(
1449 label_ptr,
1450 self.tex_ref,
1451 sys::ImPlot3DPoint_c {
1452 x: self.center[0] as f64,
1453 y: self.center[1] as f64,
1454 z: self.center[2] as f64,
1455 },
1456 sys::ImPlot3DPoint_c {
1457 x: self.axis_u[0] as f64,
1458 y: self.axis_u[1] as f64,
1459 z: self.axis_u[2] as f64,
1460 },
1461 sys::ImPlot3DPoint_c {
1462 x: self.axis_v[0] as f64,
1463 y: self.axis_v[1] as f64,
1464 z: self.axis_v[2] as f64,
1465 },
1466 imvec2(self.uv0[0], self.uv0[1]),
1467 imvec2(self.uv1[0], self.uv1[1]),
1468 imvec4(self.tint[0], self.tint[1], self.tint[2], self.tint[3]),
1469 spec,
1470 );
1471 })
1472 }
1473}
1474
1475pub struct Image3DByCornersBuilder<'ui> {
1477 _ui: &'ui Plot3DUi<'ui>,
1478 label: Cow<'ui, str>,
1479 tex_ref: sys::ImTextureRef_c,
1480 p0: [f32; 3],
1481 p1: [f32; 3],
1482 p2: [f32; 3],
1483 p3: [f32; 3],
1484 uv0: [f32; 2],
1485 uv1: [f32; 2],
1486 uv2: [f32; 2],
1487 uv3: [f32; 2],
1488 tint: [f32; 4],
1489 flags: Image3DFlags,
1490 item_flags: Item3DFlags,
1491 style: Plot3DItemStyle,
1492}
1493
1494impl<'ui> Image3DByCornersBuilder<'ui> {
1495 pub fn uvs(mut self, uv0: [f32; 2], uv1: [f32; 2], uv2: [f32; 2], uv3: [f32; 2]) -> Self {
1496 self.uv0 = uv0;
1497 self.uv1 = uv1;
1498 self.uv2 = uv2;
1499 self.uv3 = uv3;
1500 self
1501 }
1502 pub fn tint(mut self, col: [f32; 4]) -> Self {
1503 self.tint = col;
1504 self
1505 }
1506 pub fn flags(mut self, flags: Image3DFlags) -> Self {
1507 self.flags = flags;
1508 self
1509 }
1510 pub fn plot(self) {
1511 let label = self.label.as_ref();
1512 let label = if label.contains('\0') { "image" } else { label };
1513 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
1514 debug_before_plot();
1515 let spec = plot3d_spec_with_style(
1516 self.style,
1517 self.flags.bits() | self.item_flags.bits(),
1518 0,
1519 IMPLOT3D_AUTO,
1520 );
1521 sys::ImPlot3D_PlotImage_Plot3DPoint(
1522 label_ptr,
1523 self.tex_ref,
1524 sys::ImPlot3DPoint_c {
1525 x: self.p0[0] as f64,
1526 y: self.p0[1] as f64,
1527 z: self.p0[2] as f64,
1528 },
1529 sys::ImPlot3DPoint_c {
1530 x: self.p1[0] as f64,
1531 y: self.p1[1] as f64,
1532 z: self.p1[2] as f64,
1533 },
1534 sys::ImPlot3DPoint_c {
1535 x: self.p2[0] as f64,
1536 y: self.p2[1] as f64,
1537 z: self.p2[2] as f64,
1538 },
1539 sys::ImPlot3DPoint_c {
1540 x: self.p3[0] as f64,
1541 y: self.p3[1] as f64,
1542 z: self.p3[2] as f64,
1543 },
1544 imvec2(self.uv0[0], self.uv0[1]),
1545 imvec2(self.uv1[0], self.uv1[1]),
1546 imvec2(self.uv2[0], self.uv2[1]),
1547 imvec2(self.uv3[0], self.uv3[1]),
1548 imvec4(self.tint[0], self.tint[1], self.tint[2], self.tint[3]),
1549 spec,
1550 );
1551 })
1552 }
1553}
1554
1555impl<'ui> Plot3DUi<'ui> {
1556 pub fn image_by_axes<T: Into<TextureRef>>(
1558 &'ui self,
1559 label: impl Into<Cow<'ui, str>>,
1560 tex: T,
1561 center: [f32; 3],
1562 axis_u: [f32; 3],
1563 axis_v: [f32; 3],
1564 ) -> Image3DByAxesBuilder<'ui> {
1565 let tr = tex.into().raw();
1566 let tex_ref = sys::ImTextureRef_c {
1567 _TexData: tr._TexData as *mut sys::ImTextureData,
1568 _TexID: tr._TexID as sys::ImTextureID,
1569 };
1570 debug_before_plot();
1571 Image3DByAxesBuilder {
1572 _ui: self,
1573 label: label.into(),
1574 tex_ref,
1575 center,
1576 axis_u,
1577 axis_v,
1578 uv0: [0.0, 0.0],
1579 uv1: [1.0, 1.0],
1580 tint: [1.0, 1.0, 1.0, 1.0],
1581 flags: Image3DFlags::NONE,
1582 item_flags: Item3DFlags::NONE,
1583 style: Plot3DItemStyle::default(),
1584 }
1585 }
1586
1587 pub fn image_by_corners<T: Into<TextureRef>>(
1589 &'ui self,
1590 label: impl Into<Cow<'ui, str>>,
1591 tex: T,
1592 p0: [f32; 3],
1593 p1: [f32; 3],
1594 p2: [f32; 3],
1595 p3: [f32; 3],
1596 ) -> Image3DByCornersBuilder<'ui> {
1597 let tr = tex.into().raw();
1598 let tex_ref = sys::ImTextureRef_c {
1599 _TexData: tr._TexData as *mut sys::ImTextureData,
1600 _TexID: tr._TexID as sys::ImTextureID,
1601 };
1602 debug_before_plot();
1603 Image3DByCornersBuilder {
1604 _ui: self,
1605 label: label.into(),
1606 tex_ref,
1607 p0,
1608 p1,
1609 p2,
1610 p3,
1611 uv0: [0.0, 0.0],
1612 uv1: [1.0, 0.0],
1613 uv2: [1.0, 1.0],
1614 uv3: [0.0, 1.0],
1615 tint: [1.0, 1.0, 1.0, 1.0],
1616 flags: Image3DFlags::NONE,
1617 item_flags: Item3DFlags::NONE,
1618 style: Plot3DItemStyle::default(),
1619 }
1620 }
1621}
1622
1623impl<'ui> Plot3DUi<'ui> {
1625 pub fn setup_axes(
1626 &self,
1627 x_label: &str,
1628 y_label: &str,
1629 z_label: &str,
1630 x_flags: Axis3DFlags,
1631 y_flags: Axis3DFlags,
1632 z_flags: Axis3DFlags,
1633 ) {
1634 debug_before_setup();
1635 if x_label.contains('\0') || y_label.contains('\0') || z_label.contains('\0') {
1636 return;
1637 }
1638 dear_imgui_rs::with_scratch_txt_three(
1639 x_label,
1640 y_label,
1641 z_label,
1642 |x_ptr, y_ptr, z_ptr| unsafe {
1643 sys::ImPlot3D_SetupAxes(
1644 x_ptr,
1645 y_ptr,
1646 z_ptr,
1647 x_flags.bits() as i32,
1648 y_flags.bits() as i32,
1649 z_flags.bits() as i32,
1650 )
1651 },
1652 )
1653 }
1654
1655 pub fn setup_axis(&self, axis: Axis3D, label: &str, flags: Axis3DFlags) {
1656 debug_before_setup();
1657 if label.contains('\0') {
1658 return;
1659 }
1660 dear_imgui_rs::with_scratch_txt(label, |ptr| unsafe {
1661 sys::ImPlot3D_SetupAxis(axis as i32, ptr, flags.bits() as i32)
1662 })
1663 }
1664
1665 pub fn setup_axis_limits(&self, axis: Axis3D, min: f64, max: f64, cond: Plot3DCond) {
1666 debug_before_setup();
1667 unsafe { sys::ImPlot3D_SetupAxisLimits(axis as i32, min, max, cond as i32) }
1668 }
1669
1670 pub fn setup_axes_limits(
1671 &self,
1672 x_min: f64,
1673 x_max: f64,
1674 y_min: f64,
1675 y_max: f64,
1676 z_min: f64,
1677 z_max: f64,
1678 cond: Plot3DCond,
1679 ) {
1680 debug_before_setup();
1681 unsafe {
1682 sys::ImPlot3D_SetupAxesLimits(x_min, x_max, y_min, y_max, z_min, z_max, cond as i32)
1683 }
1684 }
1685
1686 pub fn setup_axis_limits_constraints(&self, axis: Axis3D, v_min: f64, v_max: f64) {
1687 debug_before_setup();
1688 unsafe { sys::ImPlot3D_SetupAxisLimitsConstraints(axis as i32, v_min, v_max) }
1689 }
1690
1691 pub fn setup_axis_zoom_constraints(&self, axis: Axis3D, z_min: f64, z_max: f64) {
1692 debug_before_setup();
1693 unsafe { sys::ImPlot3D_SetupAxisZoomConstraints(axis as i32, z_min, z_max) }
1694 }
1695
1696 pub fn setup_axis_ticks_values(
1700 &self,
1701 axis: Axis3D,
1702 values: &[f64],
1703 labels: Option<&[&str]>,
1704 keep_default: bool,
1705 ) {
1706 debug_before_setup();
1707 let Some(n_ticks) = len_i32(values.len()) else {
1708 return;
1709 };
1710 if let Some(lbls) = labels {
1711 if lbls.len() != values.len() {
1712 return;
1713 }
1714 let cleaned: Vec<&str> = lbls
1715 .iter()
1716 .map(|&s| if s.contains('\0') { "" } else { s })
1717 .collect();
1718 dear_imgui_rs::with_scratch_txt_slice(&cleaned, |ptrs| unsafe {
1719 sys::ImPlot3D_SetupAxisTicks_doublePtr(
1720 axis as i32,
1721 values.as_ptr(),
1722 n_ticks,
1723 ptrs.as_ptr(),
1724 keep_default,
1725 )
1726 });
1727 } else {
1728 unsafe {
1729 sys::ImPlot3D_SetupAxisTicks_doublePtr(
1730 axis as i32,
1731 values.as_ptr(),
1732 n_ticks,
1733 std::ptr::null(),
1734 keep_default,
1735 )
1736 };
1737 }
1738 }
1739
1740 pub fn setup_axis_ticks_range(
1741 &self,
1742 axis: Axis3D,
1743 v_min: f64,
1744 v_max: f64,
1745 n_ticks: i32,
1746 labels: Option<&[&str]>,
1747 keep_default: bool,
1748 ) {
1749 debug_before_setup();
1750 if let Some(lbls) = labels {
1751 let cleaned: Vec<&str> = lbls
1752 .iter()
1753 .map(|&s| if s.contains('\0') { "" } else { s })
1754 .collect();
1755 dear_imgui_rs::with_scratch_txt_slice(&cleaned, |ptrs| unsafe {
1756 sys::ImPlot3D_SetupAxisTicks_double(
1757 axis as i32,
1758 v_min,
1759 v_max,
1760 n_ticks,
1761 ptrs.as_ptr(),
1762 keep_default,
1763 )
1764 });
1765 } else {
1766 unsafe {
1767 sys::ImPlot3D_SetupAxisTicks_double(
1768 axis as i32,
1769 v_min,
1770 v_max,
1771 n_ticks,
1772 std::ptr::null(),
1773 keep_default,
1774 )
1775 };
1776 }
1777 }
1778
1779 pub fn setup_box_scale(&self, x: f32, y: f32, z: f32) {
1780 debug_before_setup();
1781 unsafe { sys::ImPlot3D_SetupBoxScale(x as f64, y as f64, z as f64) }
1782 }
1783
1784 pub fn setup_box_rotation(
1785 &self,
1786 elevation: f32,
1787 azimuth: f32,
1788 animate: bool,
1789 cond: Plot3DCond,
1790 ) {
1791 debug_before_setup();
1792 unsafe {
1793 sys::ImPlot3D_SetupBoxRotation_double(
1794 elevation as f64,
1795 azimuth as f64,
1796 animate,
1797 cond as i32,
1798 )
1799 }
1800 }
1801
1802 pub fn setup_box_initial_rotation(&self, elevation: f32, azimuth: f32) {
1803 debug_before_setup();
1804 unsafe { sys::ImPlot3D_SetupBoxInitialRotation_double(elevation as f64, azimuth as f64) }
1805 }
1806
1807 pub fn plot_text(&self, text: &str, x: f32, y: f32, z: f32, angle: f32, pix_offset: [f32; 2]) {
1808 if text.contains('\0') {
1809 return;
1810 }
1811 dear_imgui_rs::with_scratch_txt(text, |text_ptr| unsafe {
1812 debug_before_plot();
1813 sys::ImPlot3D_PlotText(
1814 text_ptr,
1815 x as f64,
1816 y as f64,
1817 z as f64,
1818 angle as f64,
1819 imvec2(pix_offset[0], pix_offset[1]),
1820 )
1821 })
1822 }
1823
1824 pub fn plot_to_pixels(&self, point: [f32; 3]) -> [f32; 2] {
1825 unsafe {
1826 let out = compat_ffi::ImPlot3D_PlotToPixels_double(
1827 point[0] as f64,
1828 point[1] as f64,
1829 point[2] as f64,
1830 );
1831 [out.x, out.y]
1832 }
1833 }
1834
1835 pub fn get_plot_draw_list(&self) -> *mut sys::ImDrawList {
1836 unsafe { sys::ImPlot3D_GetPlotDrawList() }
1837 }
1838
1839 pub fn get_frame_pos(&self) -> [f32; 2] {
1840 unsafe {
1841 let out = compat_ffi::ImPlot3D_GetPlotRectPos();
1842 [out.x, out.y]
1843 }
1844 }
1845
1846 pub fn get_frame_size(&self) -> [f32; 2] {
1847 unsafe {
1848 let out = compat_ffi::ImPlot3D_GetPlotRectSize();
1849 [out.x, out.y]
1850 }
1851 }
1852}
1853
1854pub struct Mesh3DBuilder<'ui> {
1856 _ui: &'ui Plot3DUi<'ui>,
1857 label: Cow<'ui, str>,
1858 vertices: &'ui [[f32; 3]],
1859 indices: &'ui [u32],
1860 flags: Mesh3DFlags,
1861 item_flags: Item3DFlags,
1862 style: Plot3DItemStyle,
1863}
1864
1865impl<'ui> Mesh3DBuilder<'ui> {
1866 pub fn flags(mut self, flags: Mesh3DFlags) -> Self {
1867 self.flags = flags;
1868 self
1869 }
1870 pub fn plot(self) {
1871 let Some(vtx_count) = len_i32(self.vertices.len()) else {
1872 return;
1873 };
1874 let Some(idx_count) = len_i32(self.indices.len()) else {
1875 return;
1876 };
1877 let mut xs = Vec::with_capacity(self.vertices.len());
1878 let mut ys = Vec::with_capacity(self.vertices.len());
1879 let mut zs = Vec::with_capacity(self.vertices.len());
1880 for [x, y, z] in self.vertices.iter().copied() {
1881 xs.push(x);
1882 ys.push(y);
1883 zs.push(z);
1884 }
1885
1886 let label = self.label.as_ref();
1887 let label = if label.contains('\0') { "mesh" } else { label };
1888 dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
1889 debug_before_plot();
1890 let spec = plot3d_spec_with_style(
1891 self.style,
1892 self.flags.bits() | self.item_flags.bits(),
1893 0,
1894 std::mem::size_of::<f32>() as i32,
1895 );
1896 sys::ImPlot3D_PlotMesh_FloatPtr(
1897 label_ptr,
1898 xs.as_ptr(),
1899 ys.as_ptr(),
1900 zs.as_ptr(),
1901 self.indices.as_ptr(),
1902 vtx_count,
1903 idx_count,
1904 spec,
1905 );
1906 })
1907 }
1908}
1909
1910impl<'ui> Plot3DUi<'ui> {
1911 pub fn mesh(
1913 &'ui self,
1914 label: impl Into<Cow<'ui, str>>,
1915 vertices: &'ui [[f32; 3]],
1916 indices: &'ui [u32],
1917 ) -> Mesh3DBuilder<'ui> {
1918 Mesh3DBuilder {
1919 _ui: self,
1920 label: label.into(),
1921 vertices,
1922 indices,
1923 flags: Mesh3DFlags::NONE,
1924 item_flags: Item3DFlags::NONE,
1925 style: Plot3DItemStyle::default(),
1926 }
1927 }
1928}
1929
1930#[cfg(test)]
1931mod tests {
1932 use super::sys;
1933 use std::mem::{align_of, size_of};
1934
1935 #[test]
1936 fn ffi_layout_implot3d_point_is_3_f64() {
1937 assert_eq!(size_of::<sys::ImPlot3DPoint>(), 3 * size_of::<f64>());
1938 assert_eq!(align_of::<sys::ImPlot3DPoint>(), align_of::<f64>());
1939 }
1940}