Skip to main content

dear_implot3d/
surface_builder.rs

1use std::borrow::Cow;
2
3use crate::item_style::{Plot3DItemStyle, plot3d_spec_with_style};
4use crate::plots::{
5    Plot3DError, SurfaceGrid, SurfaceGridShape, SurfaceLabel, submit_surface_grid,
6    submit_surface_raw,
7};
8use crate::{Item3DFlags, Plot3DDataLayout, Plot3DUi, Surface3DFlags, plot3d_spec_from};
9
10/// Surface (grid) plot builder (f32 variant).
11pub struct Surface3DBuilder<'ui> {
12    pub(crate) _ui: &'ui Plot3DUi<'ui>,
13    pub(crate) label: Cow<'ui, str>,
14    pub(crate) xs: &'ui [f32],
15    pub(crate) ys: &'ui [f32],
16    pub(crate) zs: &'ui [f32],
17    pub(crate) scale_min: f64,
18    pub(crate) scale_max: f64,
19    pub(crate) flags: Surface3DFlags,
20    pub(crate) item_flags: Item3DFlags,
21    pub(crate) style: Plot3DItemStyle,
22}
23
24impl<'ui> Surface3DBuilder<'ui> {
25    pub fn scale(mut self, min: f64, max: f64) -> Self {
26        self.scale_min = min;
27        self.scale_max = max;
28        self
29    }
30
31    pub fn flags(mut self, flags: Surface3DFlags) -> Self {
32        self.flags = flags;
33        self
34    }
35
36    /// Submit the surface after validating its complete grid shape.
37    #[must_use = "surface plot errors must be handled"]
38    pub fn plot(self) -> Result<(), Plot3DError> {
39        let Surface3DBuilder {
40            _ui,
41            label,
42            xs,
43            ys,
44            zs,
45            scale_min,
46            scale_max,
47            flags,
48            item_flags,
49            style,
50        } = self;
51
52        let label = SurfaceLabel::checked(label.as_ref())?;
53        let grid = SurfaceGrid::from_axes(xs, ys, zs)?;
54        submit_surface_grid(_ui, label, &grid, scale_min, scale_max, || {
55            plot3d_spec_with_style(
56                style,
57                flags.bits() | item_flags.bits(),
58                Plot3DDataLayout::DEFAULT,
59            )
60        });
61        Ok(())
62    }
63}
64
65impl<'ui> Plot3DUi<'ui> {
66    /// Start a surface plot (f32) from X/Y grid axes.
67    pub fn surface_f32(
68        &'ui self,
69        label: impl Into<Cow<'ui, str>>,
70        xs: &'ui [f32],
71        ys: &'ui [f32],
72        zs: &'ui [f32],
73    ) -> Surface3DBuilder<'ui> {
74        self.with_bound_context(|| Surface3DBuilder {
75            _ui: self,
76            label: label.into(),
77            xs,
78            ys,
79            zs,
80            scale_min: f64::NAN,
81            scale_max: f64::NAN,
82            flags: Surface3DFlags::NONE,
83            item_flags: Item3DFlags::NONE,
84            style: Plot3DItemStyle::default(),
85        })
86    }
87
88    /// Submit a surface from already flattened, contiguous per-vertex arrays.
89    ///
90    /// `xs_flat`, `ys_flat`, and `zs` must each contain exactly `x_count * y_count` values. The
91    /// contiguous path does not accept an offset or stride; use [`Self::surface_f32_raw`] when
92    /// the native layout is intentionally non-contiguous.
93    ///
94    /// ```no_run
95    /// use dear_implot3d::{Plot3DError, Plot3DUi, Surface3DFlags};
96    ///
97    /// fn submit(plot_ui: &Plot3DUi<'_>) -> Result<(), Plot3DError> {
98    ///     let xs = [0.0, 1.0, 0.0, 1.0];
99    ///     let ys = [0.0, 0.0, 1.0, 1.0];
100    ///     let zs = [0.0, 1.0, 1.0, 2.0];
101    ///     plot_ui.surface_f32_flat(
102    ///         "surface",
103    ///         &xs,
104    ///         &ys,
105    ///         &zs,
106    ///         2,
107    ///         2,
108    ///         0.0,
109    ///         0.0,
110    ///         Surface3DFlags::NONE,
111    ///     )
112    /// }
113    /// ```
114    #[must_use = "surface plot errors must be handled"]
115    pub fn surface_f32_flat<S: AsRef<str>>(
116        &self,
117        label: S,
118        xs_flat: &[f32],
119        ys_flat: &[f32],
120        zs: &[f32],
121        x_count: usize,
122        y_count: usize,
123        scale_min: f64,
124        scale_max: f64,
125        flags: Surface3DFlags,
126    ) -> Result<(), Plot3DError> {
127        let label = SurfaceLabel::checked(label.as_ref())?;
128        let grid = SurfaceGrid::from_flattened(xs_flat, ys_flat, zs, x_count, y_count)?;
129        submit_surface_grid(self, label, &grid, scale_min, scale_max, || {
130            plot3d_spec_from(flags.bits(), Plot3DDataLayout::DEFAULT)
131        });
132        Ok(())
133    }
134
135    /// Submit a surface with an explicitly arbitrary native data layout.
136    ///
137    /// # Safety
138    ///
139    /// The caller must ensure that every coordinate read performed by ImPlot3D from `xs`, `ys`,
140    /// and `zs` using `layout`, `x_count`, and `y_count` addresses initialized, properly aligned,
141    /// live `f32` values. The slices need not be exactly `x_count * y_count` elements because a
142    /// custom stride or offset may address a larger allocation.
143    ///
144    /// Calling this arbitrary-layout API without an `unsafe` block is rejected:
145    ///
146    /// ```compile_fail
147    /// use dear_implot3d::{Plot3DDataLayout, Plot3DUi, Surface3DFlags};
148    ///
149    /// fn submit(plot_ui: &Plot3DUi<'_>) {
150    ///     let values = [0.0; 4];
151    ///     let _ = plot_ui.surface_f32_raw(
152    ///         "surface",
153    ///         &values,
154    ///         &values,
155    ///         &values,
156    ///         2,
157    ///         2,
158    ///         0.0,
159    ///         0.0,
160    ///         Surface3DFlags::NONE,
161    ///         Plot3DDataLayout::DEFAULT,
162    ///     );
163    /// }
164    /// ```
165    #[must_use = "surface plot errors must be handled"]
166    pub unsafe fn surface_f32_raw<S: AsRef<str>>(
167        &self,
168        label: S,
169        xs: &[f32],
170        ys: &[f32],
171        zs: &[f32],
172        x_count: usize,
173        y_count: usize,
174        scale_min: f64,
175        scale_max: f64,
176        flags: Surface3DFlags,
177        layout: Plot3DDataLayout,
178    ) -> Result<(), Plot3DError> {
179        let label = SurfaceLabel::checked(label.as_ref())?;
180        let shape = SurfaceGridShape::checked(x_count, y_count)?;
181        unsafe {
182            submit_surface_raw(
183                self, label, xs, ys, zs, shape, scale_min, scale_max, flags, layout,
184            );
185        }
186        Ok(())
187    }
188}