Skip to main content

dear_implot3d/
style.rs

1use crate::flags::Marker3D;
2use crate::sys;
3use std::borrow::Cow;
4
5/// Colorable ImPlot3D style elements.
6#[repr(i32)]
7#[derive(Copy, Clone, Debug, PartialEq, Eq)]
8pub enum Plot3DColorElement {
9    TitleText = sys::ImPlot3DCol_TitleText as i32,
10    InlayText = sys::ImPlot3DCol_InlayText as i32,
11    FrameBg = sys::ImPlot3DCol_FrameBg as i32,
12    PlotBg = sys::ImPlot3DCol_PlotBg as i32,
13    PlotBorder = sys::ImPlot3DCol_PlotBorder as i32,
14    LegendBg = sys::ImPlot3DCol_LegendBg as i32,
15    LegendBorder = sys::ImPlot3DCol_LegendBorder as i32,
16    LegendText = sys::ImPlot3DCol_LegendText as i32,
17    AxisText = sys::ImPlot3DCol_AxisText as i32,
18    AxisGrid = sys::ImPlot3DCol_AxisGrid as i32,
19    AxisTick = sys::ImPlot3DCol_AxisTick as i32,
20    AxisBg = sys::ImPlot3DCol_AxisBg as i32,
21    AxisBgHovered = sys::ImPlot3DCol_AxisBgHovered as i32,
22    AxisBgActive = sys::ImPlot3DCol_AxisBgActive as i32,
23}
24
25#[inline]
26pub fn style_colors_dark() {
27    unsafe { sys::ImPlot3D_StyleColorsDark(std::ptr::null_mut()) }
28}
29#[inline]
30pub fn style_colors_light() {
31    unsafe { sys::ImPlot3D_StyleColorsLight(std::ptr::null_mut()) }
32}
33#[inline]
34pub fn style_colors_classic() {
35    unsafe { sys::ImPlot3D_StyleColorsClassic(std::ptr::null_mut()) }
36}
37#[inline]
38pub fn style_colors_auto() {
39    unsafe { sys::ImPlot3D_StyleColorsAuto(std::ptr::null_mut()) }
40}
41
42#[inline]
43pub fn push_style_color(idx: i32, col: [f32; 4]) {
44    unsafe {
45        sys::ImPlot3D_PushStyleColor_Vec4(idx, crate::imvec4(col[0], col[1], col[2], col[3]));
46    }
47}
48
49/// Push a typed style color override.
50#[inline]
51pub fn push_style_color_element(element: Plot3DColorElement, col: [f32; 4]) {
52    push_style_color(element as i32, col);
53}
54
55#[inline]
56pub fn pop_style_color(count: i32) {
57    unsafe { sys::ImPlot3D_PopStyleColor(count) }
58}
59
60/// Push a style variable (float variant)
61#[inline]
62pub fn push_style_var_f32(idx: i32, val: f32) {
63    unsafe { sys::ImPlot3D_PushStyleVar_Float(idx, val) }
64}
65
66/// Push a style variable (int variant)
67#[inline]
68pub fn push_style_var_i32(idx: i32, val: i32) {
69    unsafe { sys::ImPlot3D_PushStyleVar_Int(idx, val) }
70}
71
72/// Push a style variable (Vec2 variant)
73#[inline]
74pub fn push_style_var_vec2(idx: i32, val: [f32; 2]) {
75    unsafe { sys::ImPlot3D_PushStyleVar_Vec2(idx, crate::imvec2(val[0], val[1])) }
76}
77
78/// Pop style variable(s)
79#[inline]
80pub fn pop_style_var(count: i32) {
81    unsafe { sys::ImPlot3D_PopStyleVar(count) }
82}
83
84#[inline]
85pub fn set_next_line_style(col: [f32; 4], weight: f32) {
86    crate::update_next_plot3d_spec(|spec| {
87        spec.LineColor = crate::imvec4(col[0], col[1], col[2], col[3]);
88        spec.LineWeight = weight;
89    })
90}
91
92#[inline]
93pub fn set_next_fill_style(col: [f32; 4], alpha_mod: f32) {
94    crate::update_next_plot3d_spec(|spec| {
95        spec.FillColor = crate::imvec4(col[0], col[1], col[2], col[3]);
96        spec.FillAlpha = alpha_mod;
97    })
98}
99
100#[inline]
101pub fn set_next_marker_style(
102    marker: Marker3D,
103    size: f32,
104    fill: [f32; 4],
105    weight: f32,
106    outline: [f32; 4],
107) {
108    crate::update_next_plot3d_spec(|spec| {
109        spec.Marker = marker as sys::ImPlot3DMarker;
110        spec.MarkerSize = size;
111        spec.MarkerFillColor = crate::imvec4(fill[0], fill[1], fill[2], fill[3]);
112        spec.MarkerLineColor = crate::imvec4(outline[0], outline[1], outline[2], outline[3]);
113        spec.LineWeight = weight;
114    })
115}
116
117/// One-shot array-backed item style overrides for the next ImPlot3D submission.
118#[derive(Debug, Clone, Default, PartialEq)]
119pub struct Plot3DItemArrayStyle<'a> {
120    line_colors: Option<Cow<'a, [u32]>>,
121    fill_colors: Option<Cow<'a, [u32]>>,
122    marker_sizes: Option<Cow<'a, [f32]>>,
123    marker_line_colors: Option<Cow<'a, [u32]>>,
124    marker_fill_colors: Option<Cow<'a, [u32]>>,
125}
126
127impl<'a> Plot3DItemArrayStyle<'a> {
128    /// Create an empty array-style override.
129    pub fn new() -> Self {
130        Self::default()
131    }
132
133    /// Override per-index line colors using Dear ImGui packed colors (`ImU32` / ABGR).
134    pub fn with_line_colors(mut self, colors: &'a [u32]) -> Self {
135        self.line_colors = Some(Cow::Borrowed(colors));
136        self
137    }
138
139    /// Override per-index fill colors using Dear ImGui packed colors (`ImU32` / ABGR).
140    pub fn with_fill_colors(mut self, colors: &'a [u32]) -> Self {
141        self.fill_colors = Some(Cow::Borrowed(colors));
142        self
143    }
144
145    /// Override per-index marker sizes in pixels.
146    pub fn with_marker_sizes(mut self, sizes: &'a [f32]) -> Self {
147        self.marker_sizes = Some(Cow::Borrowed(sizes));
148        self
149    }
150
151    /// Override per-index marker outline colors using Dear ImGui packed colors (`ImU32` / ABGR).
152    pub fn with_marker_line_colors(mut self, colors: &'a [u32]) -> Self {
153        self.marker_line_colors = Some(Cow::Borrowed(colors));
154        self
155    }
156
157    /// Override per-index marker fill colors using Dear ImGui packed colors (`ImU32` / ABGR).
158    pub fn with_marker_fill_colors(mut self, colors: &'a [u32]) -> Self {
159        self.marker_fill_colors = Some(Cow::Borrowed(colors));
160        self
161    }
162
163    fn apply_to_spec(&self, spec: &mut sys::ImPlot3DSpec_c) {
164        spec.LineColors = self
165            .line_colors
166            .as_ref()
167            .map_or(std::ptr::null_mut(), |colors| colors.as_ptr() as *mut _);
168        spec.FillColors = self
169            .fill_colors
170            .as_ref()
171            .map_or(std::ptr::null_mut(), |colors| colors.as_ptr() as *mut _);
172        spec.MarkerSizes = self
173            .marker_sizes
174            .as_ref()
175            .map_or(std::ptr::null_mut(), |sizes| sizes.as_ptr() as *mut _);
176        spec.MarkerLineColors = self
177            .marker_line_colors
178            .as_ref()
179            .map_or(std::ptr::null_mut(), |colors| colors.as_ptr() as *mut _);
180        spec.MarkerFillColors = self
181            .marker_fill_colors
182            .as_ref()
183            .map_or(std::ptr::null_mut(), |colors| colors.as_ptr() as *mut _);
184    }
185}
186
187/// Apply array-backed item styling to the next ImPlot3D submission executed inside `f`.
188pub fn with_next_plot3d_item_array_style<'a, R>(
189    style: Plot3DItemArrayStyle<'a>,
190    f: impl FnOnce() -> R,
191) -> R {
192    let previous = crate::take_next_plot3d_spec();
193    let mut spec = previous.unwrap_or_else(crate::default_plot3d_spec);
194    style.apply_to_spec(&mut spec);
195    crate::set_next_plot3d_spec(Some(spec));
196
197    let out = f();
198
199    if crate::take_next_plot3d_spec().is_some() {
200        crate::set_next_plot3d_spec(previous);
201    }
202
203    out
204}
205
206#[inline]
207pub fn push_colormap_index(cmap_index: i32) {
208    unsafe { sys::ImPlot3D_PushColormap_Plot3DColormap(cmap_index) }
209}
210#[inline]
211pub fn push_colormap_name(name: &str) {
212    dear_imgui_rs::with_scratch_txt(name, |ptr| unsafe { sys::ImPlot3D_PushColormap_Str(ptr) })
213}
214#[inline]
215pub fn pop_colormap(count: i32) {
216    unsafe { sys::ImPlot3D_PopColormap(count) }
217}
218#[inline]
219pub fn colormap_count() -> i32 {
220    unsafe { sys::ImPlot3D_GetColormapCount() }
221}
222#[inline]
223pub fn colormap_name(index: i32) -> String {
224    unsafe {
225        let p = sys::ImPlot3D_GetColormapName(index);
226        if p.is_null() {
227            return String::new();
228        }
229        std::ffi::CStr::from_ptr(p).to_string_lossy().into_owned()
230    }
231}
232
233/// Get number of keys (colors) in a given colormap index
234#[inline]
235pub fn colormap_size(index: i32) -> i32 {
236    unsafe { sys::ImPlot3D_GetColormapSize(index) }
237}
238
239/// Get current default colormap index set in ImPlot3D style
240#[inline]
241pub fn get_style_colormap_index() -> i32 {
242    unsafe {
243        let style = sys::ImPlot3D_GetStyle();
244        if style.is_null() {
245            return -1;
246        }
247        (*style).Colormap
248    }
249}
250
251/// Get current default colormap name (if index valid)
252#[inline]
253pub fn get_style_colormap_name() -> Option<String> {
254    let idx = get_style_colormap_index();
255    if idx < 0 {
256        return None;
257    }
258    let count = colormap_count();
259    if idx >= count {
260        return None;
261    }
262    Some(colormap_name(idx))
263}
264
265/// Permanently set the default colormap used by ImPlot3D (persists across plots/frames)
266#[inline]
267pub fn set_style_colormap_index(index: i32) {
268    unsafe {
269        let style = sys::ImPlot3D_GetStyle();
270        if !style.is_null() {
271            let count = sys::ImPlot3D_GetColormapCount();
272            if count > 0 {
273                let idx = if index < 0 {
274                    0
275                } else if index >= count {
276                    count - 1
277                } else {
278                    index
279                };
280                (*style).Colormap = idx;
281            }
282        }
283    }
284}
285
286/// Look up a colormap index by its name; returns -1 if not found
287#[inline]
288pub fn colormap_index_by_name(name: &str) -> i32 {
289    if name.contains('\0') {
290        return -1;
291    }
292    dear_imgui_rs::with_scratch_txt(name, |ptr| unsafe { sys::ImPlot3D_GetColormapIndex(ptr) })
293}
294
295/// Convenience: set default colormap by name (no-op if name is invalid)
296#[inline]
297pub fn set_style_colormap_by_name(name: &str) {
298    let idx = colormap_index_by_name(name);
299    if idx >= 0 {
300        set_style_colormap_index(idx);
301    }
302}
303
304/// Get a color from the current colormap at index
305pub fn get_colormap_color(idx: i32) -> [f32; 4] {
306    unsafe {
307        // Pass -1 for "current" colormap (upstream convention)
308        let out = crate::compat_ffi::ImPlot3D_GetColormapColor(idx, (-1) as sys::ImPlot3DColormap);
309        [out.x, out.y, out.z, out.w]
310    }
311}
312
313/// Get next colormap color (advances internal counter)
314pub fn next_colormap_color() -> [f32; 4] {
315    unsafe {
316        let out = crate::compat_ffi::ImPlot3D_NextColormapColor();
317        [out.x, out.y, out.z, out.w]
318    }
319}
320
321#[cfg(test)]
322mod tests {
323    use super::{Plot3DItemArrayStyle, with_next_plot3d_item_array_style};
324
325    #[test]
326    fn next_plot3d_item_array_style_is_consumed_by_next_spec() {
327        let line_colors = [0x01020304u32, 0x05060708];
328        let marker_sizes = [1.5f32, 2.5];
329        let marker_fill_colors = [0x11223344u32];
330
331        with_next_plot3d_item_array_style(
332            Plot3DItemArrayStyle::new()
333                .with_line_colors(&line_colors)
334                .with_marker_sizes(&marker_sizes)
335                .with_marker_fill_colors(&marker_fill_colors),
336            || {
337                let spec = crate::plot3d_spec_from(9, 2, 24);
338                assert_eq!(spec.Flags, 9);
339                assert_eq!(spec.Offset, 2);
340                assert_eq!(spec.Stride, 24);
341                assert_eq!(spec.LineColors, line_colors.as_ptr() as *mut _);
342                assert_eq!(spec.MarkerSizes, marker_sizes.as_ptr() as *mut _);
343                assert_eq!(spec.MarkerFillColors, marker_fill_colors.as_ptr() as *mut _);
344            },
345        );
346
347        let spec = crate::plot3d_spec_from(0, 0, -1);
348        assert!(spec.LineColors.is_null());
349        assert!(spec.MarkerSizes.is_null());
350        assert!(spec.MarkerFillColors.is_null());
351    }
352
353    #[test]
354    fn next_plot3d_item_array_style_is_restored_if_unused() {
355        let fill_colors = [0xAABBCCDDu32];
356
357        with_next_plot3d_item_array_style(
358            Plot3DItemArrayStyle::new().with_fill_colors(&fill_colors),
359            || {},
360        );
361
362        let spec = crate::plot3d_spec_from(0, 0, -1);
363        assert!(spec.FillColors.is_null());
364    }
365}