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