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