Skip to main content

dear_implot/context/
axis.rs

1use super::ui::PlotUi;
2use super::validation::{
3    assert_axis_constraint_range, assert_axis_limit_range, assert_axis_zoom_range,
4    assert_finite_f64_slice, axis_tick_count_to_i32,
5};
6use crate::{Axis, AxisFlags, AxisFormat, PlotCond, XAxis, YAxis, sys};
7use dear_imgui_rs::{with_scratch_txt, with_scratch_txt_slice, with_scratch_txt_two};
8use std::os::raw::c_char;
9
10impl<'ui> PlotUi<'ui> {
11    /// Setup a specific X axis
12    pub fn setup_x_axis(&self, axis: XAxis, label: Option<&str>, flags: AxisFlags) {
13        self.with_bound_context(|| {
14            let label = label.filter(|s| !s.contains('\0'));
15            match label {
16                Some(label) => with_scratch_txt(label, |ptr| unsafe {
17                    sys::ImPlot_SetupAxis(
18                        axis as sys::ImAxis,
19                        ptr,
20                        flags.bits() as sys::ImPlotAxisFlags,
21                    )
22                }),
23                None => unsafe {
24                    sys::ImPlot_SetupAxis(
25                        axis as sys::ImAxis,
26                        std::ptr::null(),
27                        flags.bits() as sys::ImPlotAxisFlags,
28                    )
29                },
30            }
31        })
32    }
33
34    /// Setup a specific Y axis
35    pub fn setup_y_axis(&self, axis: YAxis, label: Option<&str>, flags: AxisFlags) {
36        self.with_bound_context(|| {
37            let label = label.filter(|s| !s.contains('\0'));
38            match label {
39                Some(label) => with_scratch_txt(label, |ptr| unsafe {
40                    sys::ImPlot_SetupAxis(
41                        axis as sys::ImAxis,
42                        ptr,
43                        flags.bits() as sys::ImPlotAxisFlags,
44                    )
45                }),
46                None => unsafe {
47                    sys::ImPlot_SetupAxis(
48                        axis as sys::ImAxis,
49                        std::ptr::null(),
50                        flags.bits() as sys::ImPlotAxisFlags,
51                    )
52                },
53            }
54        })
55    }
56
57    /// Setup axis limits for a specific X axis
58    pub fn setup_x_axis_limits(&self, axis: XAxis, min: f64, max: f64, cond: PlotCond) {
59        assert_axis_limit_range("PlotUi::setup_x_axis_limits()", min, max);
60        self.with_bound_context(|| unsafe {
61            sys::ImPlot_SetupAxisLimits(axis as sys::ImAxis, min, max, cond as sys::ImPlotCond)
62        })
63    }
64
65    /// Setup axis limits for a specific Y axis
66    pub fn setup_y_axis_limits(&self, axis: YAxis, min: f64, max: f64, cond: PlotCond) {
67        assert_axis_limit_range("PlotUi::setup_y_axis_limits()", min, max);
68        self.with_bound_context(|| unsafe {
69            sys::ImPlot_SetupAxisLimits(axis as sys::ImAxis, min, max, cond as sys::ImPlotCond)
70        })
71    }
72
73    /// Link an axis to external min/max values (live binding)
74    pub fn setup_axis_links(
75        &self,
76        axis: Axis,
77        link_min: Option<&mut f64>,
78        link_max: Option<&mut f64>,
79    ) {
80        let pmin = link_min.map_or(std::ptr::null_mut(), |r| r as *mut f64);
81        let pmax = link_max.map_or(std::ptr::null_mut(), |r| r as *mut f64);
82        self.with_bound_context(|| unsafe { sys::ImPlot_SetupAxisLinks(axis.to_sys(), pmin, pmax) })
83    }
84
85    /// Link a raw axis to external min/max values (live binding).
86    ///
87    /// # Safety
88    ///
89    /// `axis` must be a valid ImPlot `ImAxis` value for the active plot. Passing an
90    /// out-of-range value lets ImPlot index internal axis arrays out of bounds.
91    pub unsafe fn setup_axis_links_unchecked(
92        &self,
93        axis: sys::ImAxis,
94        link_min: Option<&mut f64>,
95        link_max: Option<&mut f64>,
96    ) {
97        let pmin = link_min.map_or(std::ptr::null_mut(), |r| r as *mut f64);
98        let pmax = link_max.map_or(std::ptr::null_mut(), |r| r as *mut f64);
99        self.with_bound_context(|| unsafe { sys::ImPlot_SetupAxisLinks(axis, pmin, pmax) })
100    }
101
102    /// Setup both axes labels/flags at once
103    pub fn setup_axes(
104        &self,
105        x_label: Option<&str>,
106        y_label: Option<&str>,
107        x_flags: AxisFlags,
108        y_flags: AxisFlags,
109    ) {
110        self.with_bound_context(|| {
111            let x_label = x_label.filter(|s| !s.contains('\0'));
112            let y_label = y_label.filter(|s| !s.contains('\0'));
113
114            match (x_label, y_label) {
115                (Some(x_label), Some(y_label)) => {
116                    with_scratch_txt_two(x_label, y_label, |xp, yp| unsafe {
117                        sys::ImPlot_SetupAxes(
118                            xp,
119                            yp,
120                            x_flags.bits() as sys::ImPlotAxisFlags,
121                            y_flags.bits() as sys::ImPlotAxisFlags,
122                        )
123                    })
124                }
125                (Some(x_label), None) => with_scratch_txt(x_label, |xp| unsafe {
126                    sys::ImPlot_SetupAxes(
127                        xp,
128                        std::ptr::null(),
129                        x_flags.bits() as sys::ImPlotAxisFlags,
130                        y_flags.bits() as sys::ImPlotAxisFlags,
131                    )
132                }),
133                (None, Some(y_label)) => with_scratch_txt(y_label, |yp| unsafe {
134                    sys::ImPlot_SetupAxes(
135                        std::ptr::null(),
136                        yp,
137                        x_flags.bits() as sys::ImPlotAxisFlags,
138                        y_flags.bits() as sys::ImPlotAxisFlags,
139                    )
140                }),
141                (None, None) => unsafe {
142                    sys::ImPlot_SetupAxes(
143                        std::ptr::null(),
144                        std::ptr::null(),
145                        x_flags.bits() as sys::ImPlotAxisFlags,
146                        y_flags.bits() as sys::ImPlotAxisFlags,
147                    )
148                },
149            }
150        })
151    }
152
153    /// Setup axes limits (both) at once
154    pub fn setup_axes_limits(
155        &self,
156        x_min: f64,
157        x_max: f64,
158        y_min: f64,
159        y_max: f64,
160        cond: PlotCond,
161    ) {
162        assert_axis_limit_range("PlotUi::setup_axes_limits() x axis", x_min, x_max);
163        assert_axis_limit_range("PlotUi::setup_axes_limits() y axis", y_min, y_max);
164        self.with_bound_context(|| unsafe {
165            sys::ImPlot_SetupAxesLimits(x_min, x_max, y_min, y_max, cond as sys::ImPlotCond)
166        })
167    }
168
169    /// Call after axis setup to finalize configuration
170    pub fn setup_finish(&self) {
171        self.with_bound_context(|| unsafe { sys::ImPlot_SetupFinish() })
172    }
173
174    /// Set next frame limits for a specific axis
175    pub fn set_next_x_axis_limits(&self, axis: XAxis, min: f64, max: f64, cond: PlotCond) {
176        assert_axis_limit_range("PlotUi::set_next_x_axis_limits()", min, max);
177        self.with_bound_context(|| unsafe {
178            sys::ImPlot_SetNextAxisLimits(axis as sys::ImAxis, min, max, cond as sys::ImPlotCond)
179        })
180    }
181
182    /// Set next frame limits for a specific axis
183    pub fn set_next_y_axis_limits(&self, axis: YAxis, min: f64, max: f64, cond: PlotCond) {
184        assert_axis_limit_range("PlotUi::set_next_y_axis_limits()", min, max);
185        self.with_bound_context(|| unsafe {
186            sys::ImPlot_SetNextAxisLimits(axis as sys::ImAxis, min, max, cond as sys::ImPlotCond)
187        })
188    }
189
190    /// Link an axis to external min/max for next frame
191    pub fn set_next_axis_links(
192        &self,
193        axis: Axis,
194        link_min: Option<&mut f64>,
195        link_max: Option<&mut f64>,
196    ) {
197        let pmin = link_min.map_or(std::ptr::null_mut(), |r| r as *mut f64);
198        let pmax = link_max.map_or(std::ptr::null_mut(), |r| r as *mut f64);
199        self.with_bound_context(|| unsafe {
200            sys::ImPlot_SetNextAxisLinks(axis.to_sys(), pmin, pmax)
201        })
202    }
203
204    /// Link a raw axis to external min/max for the next frame.
205    ///
206    /// # Safety
207    ///
208    /// `axis` must be a valid ImPlot `ImAxis` value. Passing an out-of-range
209    /// value lets ImPlot index internal next-plot arrays out of bounds.
210    pub unsafe fn set_next_axis_links_unchecked(
211        &self,
212        axis: sys::ImAxis,
213        link_min: Option<&mut f64>,
214        link_max: Option<&mut f64>,
215    ) {
216        let pmin = link_min.map_or(std::ptr::null_mut(), |r| r as *mut f64);
217        let pmax = link_max.map_or(std::ptr::null_mut(), |r| r as *mut f64);
218        self.with_bound_context(|| unsafe { sys::ImPlot_SetNextAxisLinks(axis, pmin, pmax) })
219    }
220
221    /// Set next frame limits for both axes
222    pub fn set_next_axes_limits(
223        &self,
224        x_min: f64,
225        x_max: f64,
226        y_min: f64,
227        y_max: f64,
228        cond: PlotCond,
229    ) {
230        assert_axis_limit_range("PlotUi::set_next_axes_limits() x axis", x_min, x_max);
231        assert_axis_limit_range("PlotUi::set_next_axes_limits() y axis", y_min, y_max);
232        self.with_bound_context(|| unsafe {
233            sys::ImPlot_SetNextAxesLimits(x_min, x_max, y_min, y_max, cond as sys::ImPlotCond)
234        })
235    }
236
237    /// Fit next frame both axes
238    pub fn set_next_axes_to_fit(&self) {
239        self.with_bound_context(|| unsafe { sys::ImPlot_SetNextAxesToFit() })
240    }
241
242    /// Fit next frame a specific axis
243    pub fn set_next_axis_to_fit(&self, axis: Axis) {
244        self.with_bound_context(|| unsafe { sys::ImPlot_SetNextAxisToFit(axis.to_sys()) })
245    }
246
247    /// Fit next frame a raw axis.
248    ///
249    /// # Safety
250    ///
251    /// `axis` must be a valid ImPlot `ImAxis` value. Passing an out-of-range
252    /// value lets ImPlot index internal next-plot arrays out of bounds.
253    pub unsafe fn set_next_axis_to_fit_unchecked(&self, axis: sys::ImAxis) {
254        self.with_bound_context(|| unsafe { sys::ImPlot_SetNextAxisToFit(axis) })
255    }
256
257    /// Fit next frame a specific X axis
258    pub fn set_next_x_axis_to_fit(&self, axis: XAxis) {
259        self.with_bound_context(|| unsafe { sys::ImPlot_SetNextAxisToFit(axis as sys::ImAxis) })
260    }
261
262    /// Fit next frame a specific Y axis
263    pub fn set_next_y_axis_to_fit(&self, axis: YAxis) {
264        self.with_bound_context(|| unsafe { sys::ImPlot_SetNextAxisToFit(axis as sys::ImAxis) })
265    }
266
267    /// Setup ticks with explicit positions and optional labels for an X axis.
268    ///
269    /// If `labels` is provided, it must have the same length as `values`.
270    pub fn setup_x_axis_ticks_positions(
271        &self,
272        axis: XAxis,
273        values: &[f64],
274        labels: Option<&[&str]>,
275        keep_default: bool,
276    ) {
277        assert_finite_f64_slice("PlotUi::setup_x_axis_ticks_positions()", "values", values);
278        self.with_bound_context(|| {
279            let count = match i32::try_from(values.len()) {
280                Ok(v) => v,
281                Err(_) => return,
282            };
283            if let Some(labels) = labels {
284                if labels.len() != values.len() {
285                    return;
286                }
287                let cleaned: Vec<&str> = labels
288                    .iter()
289                    .map(|&s| if s.contains('\0') { "" } else { s })
290                    .collect();
291                with_scratch_txt_slice(&cleaned, |ptrs| unsafe {
292                    sys::ImPlot_SetupAxisTicks_doublePtr(
293                        axis as sys::ImAxis,
294                        values.as_ptr(),
295                        count,
296                        ptrs.as_ptr() as *const *const c_char,
297                        keep_default,
298                    )
299                })
300            } else {
301                unsafe {
302                    sys::ImPlot_SetupAxisTicks_doublePtr(
303                        axis as sys::ImAxis,
304                        values.as_ptr(),
305                        count,
306                        std::ptr::null(),
307                        keep_default,
308                    )
309                }
310            }
311        })
312    }
313
314    /// Setup ticks with explicit positions and optional labels for a Y axis.
315    ///
316    /// If `labels` is provided, it must have the same length as `values`.
317    pub fn setup_y_axis_ticks_positions(
318        &self,
319        axis: YAxis,
320        values: &[f64],
321        labels: Option<&[&str]>,
322        keep_default: bool,
323    ) {
324        assert_finite_f64_slice("PlotUi::setup_y_axis_ticks_positions()", "values", values);
325        self.with_bound_context(|| {
326            let count = match i32::try_from(values.len()) {
327                Ok(v) => v,
328                Err(_) => return,
329            };
330            if let Some(labels) = labels {
331                if labels.len() != values.len() {
332                    return;
333                }
334                let cleaned: Vec<&str> = labels
335                    .iter()
336                    .map(|&s| if s.contains('\0') { "" } else { s })
337                    .collect();
338                with_scratch_txt_slice(&cleaned, |ptrs| unsafe {
339                    sys::ImPlot_SetupAxisTicks_doublePtr(
340                        axis as sys::ImAxis,
341                        values.as_ptr(),
342                        count,
343                        ptrs.as_ptr() as *const *const c_char,
344                        keep_default,
345                    )
346                })
347            } else {
348                unsafe {
349                    sys::ImPlot_SetupAxisTicks_doublePtr(
350                        axis as sys::ImAxis,
351                        values.as_ptr(),
352                        count,
353                        std::ptr::null(),
354                        keep_default,
355                    )
356                }
357            }
358        })
359    }
360
361    /// Setup ticks on a range with tick count and optional labels for an X axis.
362    ///
363    /// If `labels` is provided, it must have length `n_ticks`.
364    pub fn setup_x_axis_ticks_range(
365        &self,
366        axis: XAxis,
367        v_min: f64,
368        v_max: f64,
369        n_ticks: usize,
370        labels: Option<&[&str]>,
371        keep_default: bool,
372    ) {
373        assert_axis_limit_range("PlotUi::setup_x_axis_ticks_range()", v_min, v_max);
374        let n_ticks_i32 = axis_tick_count_to_i32("PlotUi::setup_x_axis_ticks_range()", n_ticks);
375        self.with_bound_context(|| {
376            if let Some(labels) = labels {
377                if labels.len() != n_ticks {
378                    return;
379                }
380                let cleaned: Vec<&str> = labels
381                    .iter()
382                    .map(|&s| if s.contains('\0') { "" } else { s })
383                    .collect();
384                with_scratch_txt_slice(&cleaned, |ptrs| unsafe {
385                    sys::ImPlot_SetupAxisTicks_double(
386                        axis as sys::ImAxis,
387                        v_min,
388                        v_max,
389                        n_ticks_i32,
390                        ptrs.as_ptr() as *const *const c_char,
391                        keep_default,
392                    )
393                })
394            } else {
395                unsafe {
396                    sys::ImPlot_SetupAxisTicks_double(
397                        axis as sys::ImAxis,
398                        v_min,
399                        v_max,
400                        n_ticks_i32,
401                        std::ptr::null(),
402                        keep_default,
403                    )
404                }
405            }
406        })
407    }
408
409    /// Setup ticks on a range with tick count and optional labels for a Y axis.
410    ///
411    /// If `labels` is provided, it must have length `n_ticks`.
412    pub fn setup_y_axis_ticks_range(
413        &self,
414        axis: YAxis,
415        v_min: f64,
416        v_max: f64,
417        n_ticks: usize,
418        labels: Option<&[&str]>,
419        keep_default: bool,
420    ) {
421        assert_axis_limit_range("PlotUi::setup_y_axis_ticks_range()", v_min, v_max);
422        let n_ticks_i32 = axis_tick_count_to_i32("PlotUi::setup_y_axis_ticks_range()", n_ticks);
423        self.with_bound_context(|| {
424            if let Some(labels) = labels {
425                if labels.len() != n_ticks {
426                    return;
427                }
428                let cleaned: Vec<&str> = labels
429                    .iter()
430                    .map(|&s| if s.contains('\0') { "" } else { s })
431                    .collect();
432                with_scratch_txt_slice(&cleaned, |ptrs| unsafe {
433                    sys::ImPlot_SetupAxisTicks_double(
434                        axis as sys::ImAxis,
435                        v_min,
436                        v_max,
437                        n_ticks_i32,
438                        ptrs.as_ptr() as *const *const c_char,
439                        keep_default,
440                    )
441                })
442            } else {
443                unsafe {
444                    sys::ImPlot_SetupAxisTicks_double(
445                        axis as sys::ImAxis,
446                        v_min,
447                        v_max,
448                        n_ticks_i32,
449                        std::ptr::null(),
450                        keep_default,
451                    )
452                }
453            }
454        })
455    }
456
457    /// Setup a validated tick label format for a specific X axis.
458    pub fn setup_x_axis_format(&self, axis: XAxis, format: &AxisFormat<'_>) {
459        self.with_bound_context(|| {
460            with_scratch_txt(format.as_str(), |ptr| unsafe {
461                sys::ImPlot_SetupAxisFormat_Str(axis as sys::ImAxis, ptr)
462            })
463        })
464    }
465
466    /// Setup a validated tick label format for a specific Y axis.
467    pub fn setup_y_axis_format(&self, axis: YAxis, format: &AxisFormat<'_>) {
468        self.with_bound_context(|| {
469            with_scratch_txt(format.as_str(), |ptr| unsafe {
470                sys::ImPlot_SetupAxisFormat_Str(axis as sys::ImAxis, ptr)
471            })
472        })
473    }
474
475    /// Setup scale for a specific X axis (pass sys::ImPlotScale variant)
476    pub fn setup_x_axis_scale(&self, axis: XAxis, scale: sys::ImPlotScale) {
477        self.with_bound_context(|| unsafe {
478            sys::ImPlot_SetupAxisScale_PlotScale(axis as sys::ImAxis, scale)
479        })
480    }
481
482    /// Setup scale for a specific Y axis (pass sys::ImPlotScale variant)
483    pub fn setup_y_axis_scale(&self, axis: YAxis, scale: sys::ImPlotScale) {
484        self.with_bound_context(|| unsafe {
485            sys::ImPlot_SetupAxisScale_PlotScale(axis as sys::ImAxis, scale)
486        })
487    }
488
489    /// Setup axis limits constraints
490    pub fn setup_axis_limits_constraints(&self, axis: Axis, v_min: f64, v_max: f64) {
491        assert_axis_constraint_range("PlotUi::setup_axis_limits_constraints()", v_min, v_max);
492        self.with_bound_context(|| unsafe {
493            sys::ImPlot_SetupAxisLimitsConstraints(axis.to_sys(), v_min, v_max)
494        })
495    }
496
497    /// Setup raw axis limits constraints.
498    ///
499    /// # Safety
500    ///
501    /// `axis` must be a valid ImPlot `ImAxis` value for the active plot. Passing an
502    /// out-of-range value lets ImPlot index internal axis arrays out of bounds.
503    pub unsafe fn setup_axis_limits_constraints_unchecked(
504        &self,
505        axis: sys::ImAxis,
506        v_min: f64,
507        v_max: f64,
508    ) {
509        assert_axis_constraint_range(
510            "PlotUi::setup_axis_limits_constraints_unchecked()",
511            v_min,
512            v_max,
513        );
514        self.with_bound_context(|| unsafe {
515            sys::ImPlot_SetupAxisLimitsConstraints(axis, v_min, v_max)
516        })
517    }
518
519    /// Setup axis zoom constraints
520    pub fn setup_axis_zoom_constraints(&self, axis: Axis, z_min: f64, z_max: f64) {
521        assert_axis_zoom_range("PlotUi::setup_axis_zoom_constraints()", z_min, z_max);
522        self.with_bound_context(|| unsafe {
523            sys::ImPlot_SetupAxisZoomConstraints(axis.to_sys(), z_min, z_max)
524        })
525    }
526
527    /// Setup raw axis zoom constraints.
528    ///
529    /// # Safety
530    ///
531    /// `axis` must be a valid ImPlot `ImAxis` value for the active plot. Passing an
532    /// out-of-range value lets ImPlot index internal axis arrays out of bounds.
533    pub unsafe fn setup_axis_zoom_constraints_unchecked(
534        &self,
535        axis: sys::ImAxis,
536        z_min: f64,
537        z_max: f64,
538    ) {
539        assert_axis_zoom_range(
540            "PlotUi::setup_axis_zoom_constraints_unchecked()",
541            z_min,
542            z_max,
543        );
544        self.with_bound_context(|| unsafe {
545            sys::ImPlot_SetupAxisZoomConstraints(axis, z_min, z_max)
546        })
547    }
548}