Skip to main content

dear_implot3d/ui/
core.rs

1use std::marker::PhantomData;
2
3use crate::builder::Plot3DBuilder;
4use crate::{
5    Line3DFlags, Plot3DDataLayout, Plot3DFlags, Quad3DFlags, Scatter3DFlags, Triangle3DFlags,
6    len_i32, plot3d_spec_from, sys,
7};
8use dear_imgui_rs::Ui;
9
10use super::binding::Plot3DContextBinding;
11
12/// Per-frame access helper mirroring `dear-implot`
13///
14/// This provides access to all 3D plotting functions. It is tied to the lifetime
15/// of the current ImGui frame and should be obtained via `Plot3DContext::get_plot_ui()`.
16///
17/// # Example
18///
19/// ```no_run
20/// use dear_implot3d::*;
21///
22/// # let plot_ui: Plot3DUi = todo!();
23/// if let Some(_token) = plot_ui.begin_plot("My 3D Plot").build() {
24///     plot_ui.setup_axes("X", "Y", "Z", Axis3DFlags::NONE, Axis3DFlags::NONE, Axis3DFlags::NONE);
25///
26///     let xs = [0.0, 1.0, 2.0];
27///     let ys = [0.0, 1.0, 0.0];
28///     let zs = [0.0, 0.5, 1.0];
29///     plot_ui.plot_line_f32("Line", &xs, &ys, &zs, Line3DFlags::NONE);
30/// }
31/// ```
32pub struct Plot3DUi<'ui> {
33    pub(crate) _ui: &'ui Ui,
34    pub(crate) binding: Plot3DContextBinding,
35    pub(crate) imgui_alive: Option<dear_imgui_rs::ContextAliveToken>,
36}
37
38impl<'ui> Plot3DUi<'ui> {
39    pub(crate) fn bind(&self) -> super::binding::Plot3DContextBindingGuard {
40        if let Some(alive) = &self.imgui_alive {
41            assert!(
42                alive.is_alive(),
43                "dear-implot3d: ImGui context has been dropped"
44            );
45        }
46        self.binding.bind()
47    }
48
49    /// Builder to configure and begin a 3D plot
50    ///
51    /// Returns a `Plot3DBuilder` that allows you to configure the plot before calling `.build()`.
52    ///
53    /// # Example
54    ///
55    /// ```no_run
56    /// use dear_implot3d::*;
57    ///
58    /// # let plot_ui: Plot3DUi = todo!();
59    /// if let Some(_token) = plot_ui
60    ///     .begin_plot("My Plot")
61    ///     .size([600.0, 400.0])
62    ///     .flags(Plot3DFlags::NO_LEGEND)
63    ///     .build()
64    /// {
65    ///     // Plot content here
66    /// }
67    /// ```
68    pub fn begin_plot<S: AsRef<str>>(&self, title: S) -> Plot3DBuilder<'ui> {
69        let _guard = self.bind();
70        Plot3DBuilder {
71            binding: self.binding,
72            imgui_alive: self.imgui_alive.clone(),
73            ui: self._ui,
74            title: title.as_ref().into(),
75            size: None,
76            flags: Plot3DFlags::empty(),
77            _lifetime: PhantomData,
78        }
79    }
80
81    /// Convenience: plot a simple 3D line (f32)
82    ///
83    /// This is a quick way to plot a line without using the builder pattern.
84    /// For more control, use the `plots::Line3D` builder.
85    ///
86    /// # Arguments
87    ///
88    /// * `label` - Label for the legend
89    /// * `xs` - X coordinates
90    /// * `ys` - Y coordinates
91    /// * `zs` - Z coordinates
92    /// * `flags` - Line flags (e.g., `Line3DFlags::SEGMENTS`, `Line3DFlags::LOOP`)
93    ///
94    /// # Example
95    ///
96    /// ```no_run
97    /// use dear_implot3d::*;
98    ///
99    /// # let plot_ui: Plot3DUi = todo!();
100    /// let xs = [0.0, 1.0, 2.0];
101    /// let ys = [0.0, 1.0, 0.0];
102    /// let zs = [0.0, 0.5, 1.0];
103    /// plot_ui.plot_line_f32("Line", &xs, &ys, &zs, Line3DFlags::NONE);
104    /// ```
105    pub fn plot_line_f32<S: AsRef<str>>(
106        &self,
107        label: S,
108        xs: &[f32],
109        ys: &[f32],
110        zs: &[f32],
111        flags: Line3DFlags,
112    ) {
113        let _guard = self.bind();
114        if xs.len() != ys.len() || ys.len() != zs.len() {
115            return;
116        }
117        let Some(count) = len_i32(xs.len()) else {
118            return;
119        };
120        let label = label.as_ref();
121        if label.contains('\0') {
122            return;
123        }
124        dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
125            let spec = plot3d_spec_from(flags.bits(), Plot3DDataLayout::DEFAULT);
126            sys::ImPlot3D_PlotLine_FloatPtr(
127                label_ptr,
128                xs.as_ptr(),
129                ys.as_ptr(),
130                zs.as_ptr(),
131                count,
132                spec,
133            );
134        })
135    }
136
137    /// Line plot (f32) with an explicit data layout.
138    pub fn plot_line_f32_raw<S: AsRef<str>>(
139        &self,
140        label: S,
141        xs: &[f32],
142        ys: &[f32],
143        zs: &[f32],
144        flags: Line3DFlags,
145        layout: Plot3DDataLayout,
146    ) {
147        let _guard = self.bind();
148        if xs.len() != ys.len() || ys.len() != zs.len() {
149            return;
150        }
151        let Some(count) = len_i32(xs.len()) else {
152            return;
153        };
154        let label = label.as_ref();
155        if label.contains('\0') {
156            return;
157        }
158        dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
159            let spec = plot3d_spec_from(flags.bits(), layout);
160            sys::ImPlot3D_PlotLine_FloatPtr(
161                label_ptr,
162                xs.as_ptr(),
163                ys.as_ptr(),
164                zs.as_ptr(),
165                count,
166                spec,
167            );
168        })
169    }
170
171    /// Convenience: plot a simple 3D line (f64)
172    pub fn plot_line_f64<S: AsRef<str>>(
173        &self,
174        label: S,
175        xs: &[f64],
176        ys: &[f64],
177        zs: &[f64],
178        flags: Line3DFlags,
179    ) {
180        let _guard = self.bind();
181        if xs.len() != ys.len() || ys.len() != zs.len() {
182            return;
183        }
184        let Some(count) = len_i32(xs.len()) else {
185            return;
186        };
187        let label = label.as_ref();
188        if label.contains('\0') {
189            return;
190        }
191        dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
192            let spec = plot3d_spec_from(flags.bits(), Plot3DDataLayout::DEFAULT);
193            sys::ImPlot3D_PlotLine_doublePtr(
194                label_ptr,
195                xs.as_ptr(),
196                ys.as_ptr(),
197                zs.as_ptr(),
198                count,
199                spec,
200            );
201        })
202    }
203
204    /// Line plot (f64) with an explicit data layout.
205    pub fn plot_line_f64_raw<S: AsRef<str>>(
206        &self,
207        label: S,
208        xs: &[f64],
209        ys: &[f64],
210        zs: &[f64],
211        flags: Line3DFlags,
212        layout: Plot3DDataLayout,
213    ) {
214        let _guard = self.bind();
215        if xs.len() != ys.len() || ys.len() != zs.len() {
216            return;
217        }
218        let Some(count) = len_i32(xs.len()) else {
219            return;
220        };
221        let label = label.as_ref();
222        if label.contains('\0') {
223            return;
224        }
225        dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
226            let spec = plot3d_spec_from(flags.bits(), layout);
227            sys::ImPlot3D_PlotLine_doublePtr(
228                label_ptr,
229                xs.as_ptr(),
230                ys.as_ptr(),
231                zs.as_ptr(),
232                count,
233                spec,
234            );
235        })
236    }
237
238    /// Convenience: plot a 3D scatter (f32)
239    pub fn plot_scatter_f32<S: AsRef<str>>(
240        &self,
241        label: S,
242        xs: &[f32],
243        ys: &[f32],
244        zs: &[f32],
245        flags: Scatter3DFlags,
246    ) {
247        let _guard = self.bind();
248        if xs.len() != ys.len() || ys.len() != zs.len() {
249            return;
250        }
251        let Some(count) = len_i32(xs.len()) else {
252            return;
253        };
254        let label = label.as_ref();
255        if label.contains('\0') {
256            return;
257        }
258        dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
259            let spec = plot3d_spec_from(flags.bits(), Plot3DDataLayout::DEFAULT);
260            sys::ImPlot3D_PlotScatter_FloatPtr(
261                label_ptr,
262                xs.as_ptr(),
263                ys.as_ptr(),
264                zs.as_ptr(),
265                count,
266                spec,
267            );
268        })
269    }
270
271    /// Scatter plot (f32) with an explicit data layout.
272    pub fn plot_scatter_f32_raw<S: AsRef<str>>(
273        &self,
274        label: S,
275        xs: &[f32],
276        ys: &[f32],
277        zs: &[f32],
278        flags: Scatter3DFlags,
279        layout: Plot3DDataLayout,
280    ) {
281        let _guard = self.bind();
282        if xs.len() != ys.len() || ys.len() != zs.len() {
283            return;
284        }
285        let Some(count) = len_i32(xs.len()) else {
286            return;
287        };
288        let label = label.as_ref();
289        if label.contains('\0') {
290            return;
291        }
292        dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
293            let spec = plot3d_spec_from(flags.bits(), layout);
294            sys::ImPlot3D_PlotScatter_FloatPtr(
295                label_ptr,
296                xs.as_ptr(),
297                ys.as_ptr(),
298                zs.as_ptr(),
299                count,
300                spec,
301            );
302        })
303    }
304
305    /// Convenience: plot a 3D scatter (f64)
306    pub fn plot_scatter_f64<S: AsRef<str>>(
307        &self,
308        label: S,
309        xs: &[f64],
310        ys: &[f64],
311        zs: &[f64],
312        flags: Scatter3DFlags,
313    ) {
314        let _guard = self.bind();
315        if xs.len() != ys.len() || ys.len() != zs.len() {
316            return;
317        }
318        let Some(count) = len_i32(xs.len()) else {
319            return;
320        };
321        let label = label.as_ref();
322        if label.contains('\0') {
323            return;
324        }
325        dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
326            let spec = plot3d_spec_from(flags.bits(), Plot3DDataLayout::DEFAULT);
327            sys::ImPlot3D_PlotScatter_doublePtr(
328                label_ptr,
329                xs.as_ptr(),
330                ys.as_ptr(),
331                zs.as_ptr(),
332                count,
333                spec,
334            );
335        })
336    }
337
338    /// Scatter plot (f64) with an explicit data layout.
339    pub fn plot_scatter_f64_raw<S: AsRef<str>>(
340        &self,
341        label: S,
342        xs: &[f64],
343        ys: &[f64],
344        zs: &[f64],
345        flags: Scatter3DFlags,
346        layout: Plot3DDataLayout,
347    ) {
348        let _guard = self.bind();
349        if xs.len() != ys.len() || ys.len() != zs.len() {
350            return;
351        }
352        let Some(count) = len_i32(xs.len()) else {
353            return;
354        };
355        let label = label.as_ref();
356        if label.contains('\0') {
357            return;
358        }
359        dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
360            let spec = plot3d_spec_from(flags.bits(), layout);
361            sys::ImPlot3D_PlotScatter_doublePtr(
362                label_ptr,
363                xs.as_ptr(),
364                ys.as_ptr(),
365                zs.as_ptr(),
366                count,
367                spec,
368            );
369        })
370    }
371
372    /// Convenience: plot triangles from interleaved xyz arrays (count must be multiple of 3)
373    pub fn plot_triangles_f32<S: AsRef<str>>(
374        &self,
375        label: S,
376        xs: &[f32],
377        ys: &[f32],
378        zs: &[f32],
379        flags: Triangle3DFlags,
380    ) {
381        let _guard = self.bind();
382        if xs.len() != ys.len() || ys.len() != zs.len() {
383            return;
384        }
385        let Some(count) = len_i32(xs.len()) else {
386            return;
387        };
388        let label = label.as_ref();
389        if label.contains('\0') {
390            return;
391        }
392        dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
393            let spec = plot3d_spec_from(flags.bits(), Plot3DDataLayout::DEFAULT);
394            sys::ImPlot3D_PlotTriangle_FloatPtr(
395                label_ptr,
396                xs.as_ptr(),
397                ys.as_ptr(),
398                zs.as_ptr(),
399                count,
400                spec,
401            );
402        })
403    }
404
405    pub fn plot_triangles_f32_raw<S: AsRef<str>>(
406        &self,
407        label: S,
408        xs: &[f32],
409        ys: &[f32],
410        zs: &[f32],
411        flags: Triangle3DFlags,
412        layout: Plot3DDataLayout,
413    ) {
414        let _guard = self.bind();
415        if xs.len() != ys.len() || ys.len() != zs.len() {
416            return;
417        }
418        let Some(count) = len_i32(xs.len()) else {
419            return;
420        };
421        let label = label.as_ref();
422        if label.contains('\0') {
423            return;
424        }
425        dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
426            let spec = plot3d_spec_from(flags.bits(), layout);
427            sys::ImPlot3D_PlotTriangle_FloatPtr(
428                label_ptr,
429                xs.as_ptr(),
430                ys.as_ptr(),
431                zs.as_ptr(),
432                count,
433                spec,
434            );
435        })
436    }
437
438    /// Convenience: plot quads from interleaved xyz arrays (count must be multiple of 4)
439    pub fn plot_quads_f32<S: AsRef<str>>(
440        &self,
441        label: S,
442        xs: &[f32],
443        ys: &[f32],
444        zs: &[f32],
445        flags: Quad3DFlags,
446    ) {
447        let _guard = self.bind();
448        if xs.len() != ys.len() || ys.len() != zs.len() {
449            return;
450        }
451        let Some(count) = len_i32(xs.len()) else {
452            return;
453        };
454        let label = label.as_ref();
455        if label.contains('\0') {
456            return;
457        }
458        dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
459            let spec = plot3d_spec_from(flags.bits(), Plot3DDataLayout::DEFAULT);
460            sys::ImPlot3D_PlotQuad_FloatPtr(
461                label_ptr,
462                xs.as_ptr(),
463                ys.as_ptr(),
464                zs.as_ptr(),
465                count,
466                spec,
467            );
468        })
469    }
470
471    pub fn plot_quads_f32_raw<S: AsRef<str>>(
472        &self,
473        label: S,
474        xs: &[f32],
475        ys: &[f32],
476        zs: &[f32],
477        flags: Quad3DFlags,
478        layout: Plot3DDataLayout,
479    ) {
480        let _guard = self.bind();
481        if xs.len() != ys.len() || ys.len() != zs.len() {
482            return;
483        }
484        let Some(count) = len_i32(xs.len()) else {
485            return;
486        };
487        let label = label.as_ref();
488        if label.contains('\0') {
489            return;
490        }
491        dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
492            let spec = plot3d_spec_from(flags.bits(), layout);
493            sys::ImPlot3D_PlotQuad_FloatPtr(
494                label_ptr,
495                xs.as_ptr(),
496                ys.as_ptr(),
497                zs.as_ptr(),
498                count,
499                spec,
500            );
501        })
502    }
503
504    /// Convenience: plot triangles from interleaved xyz arrays (f64)
505    pub fn plot_triangles_f64<S: AsRef<str>>(
506        &self,
507        label: S,
508        xs: &[f64],
509        ys: &[f64],
510        zs: &[f64],
511        flags: Triangle3DFlags,
512    ) {
513        let _guard = self.bind();
514        if xs.len() != ys.len() || ys.len() != zs.len() {
515            return;
516        }
517        let Some(count) = len_i32(xs.len()) else {
518            return;
519        };
520        let label = label.as_ref();
521        if label.contains('\0') {
522            return;
523        }
524        dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
525            let spec = plot3d_spec_from(flags.bits(), Plot3DDataLayout::DEFAULT);
526            sys::ImPlot3D_PlotTriangle_doublePtr(
527                label_ptr,
528                xs.as_ptr(),
529                ys.as_ptr(),
530                zs.as_ptr(),
531                count,
532                spec,
533            );
534        })
535    }
536
537    pub fn plot_triangles_f64_raw<S: AsRef<str>>(
538        &self,
539        label: S,
540        xs: &[f64],
541        ys: &[f64],
542        zs: &[f64],
543        flags: Triangle3DFlags,
544        layout: Plot3DDataLayout,
545    ) {
546        let _guard = self.bind();
547        if xs.len() != ys.len() || ys.len() != zs.len() {
548            return;
549        }
550        let Some(count) = len_i32(xs.len()) else {
551            return;
552        };
553        let label = label.as_ref();
554        if label.contains('\0') {
555            return;
556        }
557        dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
558            let spec = plot3d_spec_from(flags.bits(), layout);
559            sys::ImPlot3D_PlotTriangle_doublePtr(
560                label_ptr,
561                xs.as_ptr(),
562                ys.as_ptr(),
563                zs.as_ptr(),
564                count,
565                spec,
566            );
567        })
568    }
569
570    /// Convenience: plot quads from interleaved xyz arrays (f64)
571    pub fn plot_quads_f64<S: AsRef<str>>(
572        &self,
573        label: S,
574        xs: &[f64],
575        ys: &[f64],
576        zs: &[f64],
577        flags: Quad3DFlags,
578    ) {
579        let _guard = self.bind();
580        if xs.len() != ys.len() || ys.len() != zs.len() {
581            return;
582        }
583        let Some(count) = len_i32(xs.len()) else {
584            return;
585        };
586        let label = label.as_ref();
587        if label.contains('\0') {
588            return;
589        }
590        dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
591            let spec = plot3d_spec_from(flags.bits(), Plot3DDataLayout::DEFAULT);
592            sys::ImPlot3D_PlotQuad_doublePtr(
593                label_ptr,
594                xs.as_ptr(),
595                ys.as_ptr(),
596                zs.as_ptr(),
597                count,
598                spec,
599            );
600        })
601    }
602
603    pub fn plot_quads_f64_raw<S: AsRef<str>>(
604        &self,
605        label: S,
606        xs: &[f64],
607        ys: &[f64],
608        zs: &[f64],
609        flags: Quad3DFlags,
610        layout: Plot3DDataLayout,
611    ) {
612        let _guard = self.bind();
613        if xs.len() != ys.len() || ys.len() != zs.len() {
614            return;
615        }
616        let Some(count) = len_i32(xs.len()) else {
617            return;
618        };
619        let label = label.as_ref();
620        if label.contains('\0') {
621            return;
622        }
623        dear_imgui_rs::with_scratch_txt(label, |label_ptr| unsafe {
624            let spec = plot3d_spec_from(flags.bits(), layout);
625            sys::ImPlot3D_PlotQuad_doublePtr(
626                label_ptr,
627                xs.as_ptr(),
628                ys.as_ptr(),
629                zs.as_ptr(),
630                count,
631                spec,
632            );
633        })
634    }
635}