Skip to main content

dear_implot/context/
ui.rs

1use super::core::PlotContext;
2use super::token::PlotToken;
3use super::validation::assert_finite_vec2;
4use crate::{XAxis, YAxis, sys};
5use dear_imgui_rs::{Ui, with_scratch_txt};
6
7/// A temporary reference for building plots
8///
9/// This struct ensures that plots can only be created when both ImGui and ImPlot
10/// contexts are available and properly set up.
11pub struct PlotUi<'ui> {
12    #[allow(dead_code)]
13    pub(crate) context: &'ui PlotContext,
14    #[allow(dead_code)]
15    pub(crate) ui: &'ui Ui,
16}
17
18impl<'ui> PlotUi<'ui> {
19    #[inline]
20    pub(crate) fn bind(&self) -> super::core::PlotContextBindingGuard {
21        self.context.assert_imgui_alive();
22        self.context.binding().bind("dear-implot: PlotUi")
23    }
24
25    /// Begin a new plot with the given title
26    ///
27    /// Returns a PlotToken if the plot was successfully started.
28    /// The plot will be automatically ended when the token is dropped.
29    pub fn begin_plot(&self, title: &str) -> Option<PlotToken<'_>> {
30        let size = sys::ImVec2_c { x: -1.0, y: 0.0 };
31        if title.contains('\0') {
32            return None;
33        }
34        let _guard = self.bind();
35        let started = with_scratch_txt(title, |ptr| unsafe { sys::ImPlot_BeginPlot(ptr, size, 0) });
36
37        if started {
38            Some(PlotToken::new(
39                self.context.binding(),
40                self.context.imgui_alive_token(),
41                self.ui,
42            ))
43        } else {
44            None
45        }
46    }
47
48    /// Begin a plot with custom size
49    pub fn begin_plot_with_size(&self, title: &str, size: [f32; 2]) -> Option<PlotToken<'_>> {
50        assert_finite_vec2("PlotUi::begin_plot_with_size()", "size", size);
51        let plot_size = sys::ImVec2_c {
52            x: size[0],
53            y: size[1],
54        };
55        if title.contains('\0') {
56            return None;
57        }
58        let _guard = self.bind();
59        let started = with_scratch_txt(title, |ptr| unsafe {
60            sys::ImPlot_BeginPlot(ptr, plot_size, 0)
61        });
62
63        if started {
64            Some(PlotToken::new(
65                self.context.binding(),
66                self.context.imgui_alive_token(),
67                self.ui,
68            ))
69        } else {
70            None
71        }
72    }
73
74    /// Plot a line with the given label and data
75    ///
76    /// This is a convenience method that can be called within a plot.
77    pub fn plot_line(&self, label: &str, x_data: &[f64], y_data: &[f64]) {
78        if x_data.len() != y_data.len() {
79            return; // Data length mismatch
80        }
81        let count = match i32::try_from(x_data.len()) {
82            Ok(v) => v,
83            Err(_) => return,
84        };
85
86        let label = if label.contains('\0') { "" } else { label };
87        let _guard = self.bind();
88        with_scratch_txt(label, |ptr| unsafe {
89            let spec = crate::plots::plot_spec_from(0, crate::plots::PlotDataLayout::DEFAULT);
90            sys::ImPlot_PlotLine_doublePtrdoublePtr(
91                ptr,
92                x_data.as_ptr(),
93                y_data.as_ptr(),
94                count,
95                spec,
96            );
97        })
98    }
99
100    /// Plot a scatter plot with the given label and data
101    pub fn plot_scatter(&self, label: &str, x_data: &[f64], y_data: &[f64]) {
102        if x_data.len() != y_data.len() {
103            return; // Data length mismatch
104        }
105        let count = match i32::try_from(x_data.len()) {
106            Ok(v) => v,
107            Err(_) => return,
108        };
109
110        let label = if label.contains('\0') { "" } else { label };
111        let _guard = self.bind();
112        with_scratch_txt(label, |ptr| unsafe {
113            let spec = crate::plots::plot_spec_from(0, crate::plots::PlotDataLayout::DEFAULT);
114            sys::ImPlot_PlotScatter_doublePtrdoublePtr(
115                ptr,
116                x_data.as_ptr(),
117                y_data.as_ptr(),
118                count,
119                spec,
120            );
121        })
122    }
123
124    /// Plot a polygon with the given label and vertex data.
125    pub fn plot_polygon(&self, label: &str, x_data: &[f64], y_data: &[f64]) {
126        if x_data.len() != y_data.len() {
127            return;
128        }
129        let count = match i32::try_from(x_data.len()) {
130            Ok(v) => v,
131            Err(_) => return,
132        };
133
134        let label = if label.contains('\0') { "" } else { label };
135        let _guard = self.bind();
136        with_scratch_txt(label, |ptr| unsafe {
137            let spec = crate::plots::plot_spec_from(0, crate::plots::PlotDataLayout::DEFAULT);
138            sys::ImPlot_PlotPolygon_doublePtr(ptr, x_data.as_ptr(), y_data.as_ptr(), count, spec);
139        })
140    }
141
142    /// Check if the plot area is hovered
143    pub fn is_plot_hovered(&self) -> bool {
144        let _guard = self.bind();
145        unsafe { sys::ImPlot_IsPlotHovered() }
146    }
147
148    /// Get the mouse position in plot coordinates
149    pub fn get_plot_mouse_pos(&self, y_axis: Option<crate::YAxisChoice>) -> sys::ImPlotPoint {
150        let y_axis_i32 = crate::y_axis_choice_option_to_i32(y_axis);
151        let y_axis = match y_axis_i32 {
152            0 => 3,
153            1 => 4,
154            2 => 5,
155            _ => 3,
156        };
157        let _guard = self.bind();
158        unsafe { sys::ImPlot_GetPlotMousePos(0, y_axis) }
159    }
160
161    /// Get the mouse position in plot coordinates for specific axes
162    pub fn get_plot_mouse_pos_axes(&self, x_axis: XAxis, y_axis: YAxis) -> sys::ImPlotPoint {
163        let _guard = self.bind();
164        unsafe { sys::ImPlot_GetPlotMousePos(x_axis as i32, y_axis as i32) }
165    }
166
167    /// Set current axes for subsequent plot submissions
168    pub fn set_axes(&self, x_axis: XAxis, y_axis: YAxis) {
169        let _guard = self.bind();
170        unsafe { sys::ImPlot_SetAxes(x_axis as i32, y_axis as i32) }
171    }
172}