1use crate::{Axis, PlotUi, XAxis, YAxis, compat_ffi, sys};
4use dear_imgui_rs::with_scratch_txt;
5use std::fmt;
6
7fn assert_finite_f64(caller: &str, name: &str, value: f64) {
8 assert!(value.is_finite(), "{caller} {name} must be finite");
9}
10
11fn assert_finite_vec2(caller: &str, name: &str, value: [f32; 2]) {
12 assert!(
13 value[0].is_finite() && value[1].is_finite(),
14 "{caller} {name} must be finite"
15 );
16}
17
18fn assert_finite_color(caller: &str, name: &str, value: [f32; 4]) {
19 assert!(
20 value.iter().all(|component| component.is_finite()),
21 "{caller} {name} must be finite"
22 );
23}
24
25fn assert_finite_point(caller: &str, name: &str, value: sys::ImPlotPoint) {
26 assert!(
27 value.x.is_finite() && value.y.is_finite(),
28 "{caller} {name} must be finite"
29 );
30}
31
32impl PlotUi<'_> {
33 pub fn is_subplots_hovered(&self) -> bool {
35 let _guard = self.bind();
36 unsafe { sys::ImPlot_IsSubplotsHovered() }
37 }
38
39 pub fn is_legend_entry_hovered(&self, label: &str) -> bool {
41 let label = if label.contains('\0') { "" } else { label };
42 let _guard = self.bind();
43 with_scratch_txt(label, |ptr| unsafe {
44 sys::ImPlot_IsLegendEntryHovered(ptr)
45 })
46 }
47
48 pub fn plot_mouse_position(
50 &self,
51 y_axis_choice: Option<crate::YAxisChoice>,
52 ) -> sys::ImPlotPoint {
53 let x_axis = 0; let y_axis = match y_axis_choice {
55 Some(crate::YAxisChoice::First) => 3, Some(crate::YAxisChoice::Second) => 4, Some(crate::YAxisChoice::Third) => 5, None => 3, };
60 let _guard = self.bind();
61 unsafe { sys::ImPlot_GetPlotMousePos(x_axis as sys::ImAxis, y_axis as sys::ImAxis) }
62 }
63
64 pub fn plot_mouse_position_axes(&self, x_axis: XAxis, y_axis: YAxis) -> sys::ImPlotPoint {
66 let _guard = self.bind();
67 unsafe { sys::ImPlot_GetPlotMousePos(x_axis as sys::ImAxis, y_axis as sys::ImAxis) }
68 }
69
70 pub fn pixels_to_plot(
72 &self,
73 pixel_position: [f32; 2],
74 y_axis_choice: Option<crate::YAxisChoice>,
75 ) -> sys::ImPlotPoint {
76 assert_finite_vec2("PlotUi::pixels_to_plot()", "pixel_position", pixel_position);
77 let y_index = match y_axis_choice {
79 Some(crate::YAxisChoice::First) => 0,
80 Some(crate::YAxisChoice::Second) => 1,
81 Some(crate::YAxisChoice::Third) => 2,
82 None => 0,
83 };
84 let _guard = self.bind();
85 unsafe {
86 let plot = sys::ImPlot_GetCurrentPlot();
87 if plot.is_null() {
88 return sys::ImPlotPoint { x: 0.0, y: 0.0 };
89 }
90 let x_axis_ptr = sys::ImPlotPlot_XAxis_Nil(plot, 0);
91 let y_axis_ptr = sys::ImPlotPlot_YAxis_Nil(plot, y_index);
92 let x = sys::ImPlotAxis_PixelsToPlot(x_axis_ptr, pixel_position[0]);
93 let y = sys::ImPlotAxis_PixelsToPlot(y_axis_ptr, pixel_position[1]);
94 sys::ImPlotPoint { x, y }
95 }
96 }
97
98 pub fn pixels_to_plot_axes(
100 &self,
101 pixel_position: [f32; 2],
102 x_axis: XAxis,
103 y_axis: YAxis,
104 ) -> sys::ImPlotPoint {
105 assert_finite_vec2(
106 "PlotUi::pixels_to_plot_axes()",
107 "pixel_position",
108 pixel_position,
109 );
110 let _guard = self.bind();
111 unsafe {
112 let plot = sys::ImPlot_GetCurrentPlot();
113 if plot.is_null() {
114 return sys::ImPlotPoint { x: 0.0, y: 0.0 };
115 }
116 let x_axis_ptr = sys::ImPlotPlot_XAxis_Nil(plot, x_axis as i32);
117 let y_axis_ptr = sys::ImPlotPlot_YAxis_Nil(plot, y_axis.to_index());
118 let x = sys::ImPlotAxis_PixelsToPlot(x_axis_ptr, pixel_position[0]);
119 let y = sys::ImPlotAxis_PixelsToPlot(y_axis_ptr, pixel_position[1]);
120 sys::ImPlotPoint { x, y }
121 }
122 }
123
124 pub fn plot_to_pixels(
126 &self,
127 plot_position: sys::ImPlotPoint,
128 y_axis_choice: Option<crate::YAxisChoice>,
129 ) -> [f32; 2] {
130 assert_finite_point("PlotUi::plot_to_pixels()", "plot_position", plot_position);
131 let y_index = match y_axis_choice {
132 Some(crate::YAxisChoice::First) => 0,
133 Some(crate::YAxisChoice::Second) => 1,
134 Some(crate::YAxisChoice::Third) => 2,
135 None => 0,
136 };
137 let _guard = self.bind();
138 unsafe {
139 let plot = sys::ImPlot_GetCurrentPlot();
140 if plot.is_null() {
141 return [0.0, 0.0];
142 }
143 let x_axis_ptr = sys::ImPlotPlot_XAxis_Nil(plot, 0);
144 let y_axis_ptr = sys::ImPlotPlot_YAxis_Nil(plot, y_index);
145 let px = sys::ImPlotAxis_PlotToPixels(x_axis_ptr, plot_position.x);
146 let py = sys::ImPlotAxis_PlotToPixels(y_axis_ptr, plot_position.y);
147 [px, py]
148 }
149 }
150
151 pub fn plot_to_pixels_axes(
153 &self,
154 plot_position: sys::ImPlotPoint,
155 x_axis: XAxis,
156 y_axis: YAxis,
157 ) -> [f32; 2] {
158 assert_finite_point(
159 "PlotUi::plot_to_pixels_axes()",
160 "plot_position",
161 plot_position,
162 );
163 let _guard = self.bind();
164 unsafe {
165 let plot = sys::ImPlot_GetCurrentPlot();
166 if plot.is_null() {
167 return [0.0, 0.0];
168 }
169 let x_axis_ptr = sys::ImPlotPlot_XAxis_Nil(plot, x_axis as i32);
170 let y_axis_ptr = sys::ImPlotPlot_YAxis_Nil(plot, y_axis.to_index());
171 let px = sys::ImPlotAxis_PlotToPixels(x_axis_ptr, plot_position.x);
172 let py = sys::ImPlotAxis_PlotToPixels(y_axis_ptr, plot_position.y);
173 [px, py]
174 }
175 }
176
177 pub fn plot_limits(
179 &self,
180 _x_axis_choice: Option<crate::YAxisChoice>,
181 y_axis_choice: Option<crate::YAxisChoice>,
182 ) -> sys::ImPlotRect {
183 let x_axis = 0; let y_axis = match y_axis_choice {
185 Some(crate::YAxisChoice::First) => 3, Some(crate::YAxisChoice::Second) => 4, Some(crate::YAxisChoice::Third) => 5, None => 3, };
190 let _guard = self.bind();
191 unsafe { sys::ImPlot_GetPlotLimits(x_axis, y_axis) }
192 }
193
194 pub fn is_plot_selected(&self) -> bool {
196 let _guard = self.bind();
197 unsafe { sys::ImPlot_IsPlotSelected() }
198 }
199
200 pub fn plot_selection_axes(&self, x_axis: XAxis, y_axis: YAxis) -> Option<sys::ImPlotRect> {
202 if !self.is_plot_selected() {
203 return None;
204 }
205 let _guard = self.bind();
206 let rect = unsafe { sys::ImPlot_GetPlotSelection(x_axis as i32, y_axis as i32) };
207 Some(rect)
208 }
209
210 pub fn annotation_point(
212 &self,
213 x: f64,
214 y: f64,
215 color: [f32; 4],
216 pixel_offset: [f32; 2],
217 clamp: bool,
218 round: bool,
219 ) {
220 assert_finite_f64("PlotUi::annotation_point()", "x", x);
221 assert_finite_f64("PlotUi::annotation_point()", "y", y);
222 assert_finite_color("PlotUi::annotation_point()", "color", color);
223 assert_finite_vec2("PlotUi::annotation_point()", "pixel_offset", pixel_offset);
224 let col = sys::ImVec4_c {
225 x: color[0],
226 y: color[1],
227 z: color[2],
228 w: color[3],
229 };
230 let off = sys::ImVec2_c {
231 x: pixel_offset[0],
232 y: pixel_offset[1],
233 };
234 let _guard = self.bind();
235 unsafe { sys::ImPlot_Annotation_Bool(x, y, col, off, clamp, round) }
236 }
237
238 pub fn annotation_text(
243 &self,
244 x: f64,
245 y: f64,
246 color: [f32; 4],
247 pixel_offset: [f32; 2],
248 clamp: bool,
249 text: &str,
250 ) {
251 assert_finite_f64("PlotUi::annotation_text()", "x", x);
252 assert_finite_f64("PlotUi::annotation_text()", "y", y);
253 assert_finite_color("PlotUi::annotation_text()", "color", color);
254 assert_finite_vec2("PlotUi::annotation_text()", "pixel_offset", pixel_offset);
255 let col = sys::ImVec4_c {
256 x: color[0],
257 y: color[1],
258 z: color[2],
259 w: color[3],
260 };
261 let off = sys::ImVec2_c {
262 x: pixel_offset[0],
263 y: pixel_offset[1],
264 };
265 assert!(!text.contains('\0'), "text contained NUL");
266 let _guard = self.bind();
267 with_scratch_txt(text, |ptr| unsafe {
268 compat_ffi::ImPlot_Annotation_Str0(x, y, col, off, clamp, ptr)
269 })
270 }
271
272 pub fn tag_x(&self, x: f64, color: [f32; 4], round: bool) {
274 assert_finite_f64("PlotUi::tag_x()", "x", x);
275 assert_finite_color("PlotUi::tag_x()", "color", color);
276 let col = sys::ImVec4_c {
277 x: color[0],
278 y: color[1],
279 z: color[2],
280 w: color[3],
281 };
282 let _guard = self.bind();
283 unsafe { sys::ImPlot_TagX_Bool(x, col, round) }
284 }
285
286 pub fn tag_x_text(&self, x: f64, color: [f32; 4], text: &str) {
288 assert_finite_f64("PlotUi::tag_x_text()", "x", x);
289 assert_finite_color("PlotUi::tag_x_text()", "color", color);
290 let col = sys::ImVec4_c {
291 x: color[0],
292 y: color[1],
293 z: color[2],
294 w: color[3],
295 };
296 assert!(!text.contains('\0'), "text contained NUL");
297 let _guard = self.bind();
298 with_scratch_txt(text, |ptr| unsafe {
299 compat_ffi::ImPlot_TagX_Str0(x, col, ptr)
300 })
301 }
302
303 pub fn tag_y(&self, y: f64, color: [f32; 4], round: bool) {
305 assert_finite_f64("PlotUi::tag_y()", "y", y);
306 assert_finite_color("PlotUi::tag_y()", "color", color);
307 let col = sys::ImVec4_c {
308 x: color[0],
309 y: color[1],
310 z: color[2],
311 w: color[3],
312 };
313 let _guard = self.bind();
314 unsafe { sys::ImPlot_TagY_Bool(y, col, round) }
315 }
316
317 pub fn tag_y_text(&self, y: f64, color: [f32; 4], text: &str) {
319 assert_finite_f64("PlotUi::tag_y_text()", "y", y);
320 assert_finite_color("PlotUi::tag_y_text()", "color", color);
321 let col = sys::ImVec4_c {
322 x: color[0],
323 y: color[1],
324 z: color[2],
325 w: color[3],
326 };
327 assert!(!text.contains('\0'), "text contained NUL");
328 let _guard = self.bind();
329 with_scratch_txt(text, |ptr| unsafe {
330 compat_ffi::ImPlot_TagY_Str0(y, col, ptr)
331 })
332 }
333
334 pub fn plot_limits_axes(&self, x_axis: XAxis, y_axis: YAxis) -> sys::ImPlotRect {
336 let _guard = self.bind();
337 unsafe { sys::ImPlot_GetPlotLimits(x_axis as i32, y_axis as i32) }
338 }
339
340 pub fn is_axis_hovered(&self, axis: Axis) -> bool {
342 let _guard = self.bind();
343 unsafe { sys::ImPlot_IsAxisHovered(axis.to_sys()) }
344 }
345
346 pub unsafe fn is_axis_hovered_unchecked(&self, axis: sys::ImAxis) -> bool {
353 let _guard = self.bind();
354 unsafe { sys::ImPlot_IsAxisHovered(axis) }
355 }
356
357 pub fn is_plot_x_axis_hovered(&self) -> bool {
359 self.is_axis_hovered(Axis::X1)
360 }
361
362 pub fn is_plot_x_axis_hovered_axis(&self, x_axis: XAxis) -> bool {
364 self.is_axis_hovered(x_axis.into())
365 }
366
367 pub fn is_plot_y_axis_hovered(&self, y_axis_choice: Option<crate::YAxisChoice>) -> bool {
369 let axis = match y_axis_choice {
370 Some(crate::YAxisChoice::First) | None => Axis::Y1,
371 Some(crate::YAxisChoice::Second) => Axis::Y2,
372 Some(crate::YAxisChoice::Third) => Axis::Y3,
373 };
374 self.is_axis_hovered(axis)
375 }
376
377 pub fn is_plot_y_axis_hovered_axis(&self, y_axis: YAxis) -> bool {
379 self.is_axis_hovered(y_axis.into())
380 }
381
382 #[cfg(feature = "demo")]
384 pub fn show_demo_window(&self, show: &mut bool) {
385 let _guard = self.bind();
386 unsafe { sys::ImPlot_ShowDemoWindow(show) }
387 }
388
389 #[cfg(not(feature = "demo"))]
391 pub fn show_demo_window(&self, _show: &mut bool) {}
392
393 pub fn show_user_guide(&self) {
395 let _guard = self.bind();
396 unsafe { sys::ImPlot_ShowUserGuide() }
397 }
398
399 pub fn show_metrics_window(&self, open: &mut bool) {
401 let _guard = self.bind();
402 unsafe { sys::ImPlot_ShowMetricsWindow(open as *mut bool) }
403 }
404
405 pub fn plot_pos(&self) -> [f32; 2] {
407 let _guard = self.bind();
408 let out = unsafe { crate::compat_ffi::ImPlot_GetPlotPos() };
409 [out.x, out.y]
410 }
411
412 pub fn plot_size(&self) -> [f32; 2] {
414 let _guard = self.bind();
415 let out = unsafe { crate::compat_ffi::ImPlot_GetPlotSize() };
416 [out.x, out.y]
417 }
418}
419
420#[derive(Debug, Clone, Copy, Default)]
422pub struct DragResult {
423 pub changed: bool,
425 pub clicked: bool,
427 pub hovered: bool,
429 pub held: bool,
431}
432
433#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
438#[repr(transparent)]
439pub struct DragToolId(i32);
440
441impl DragToolId {
442 #[inline]
444 pub const fn new(id: i32) -> Self {
445 Self(id)
446 }
447
448 #[inline]
450 pub const fn raw(self) -> i32 {
451 self.0
452 }
453}
454
455impl From<i32> for DragToolId {
456 #[inline]
457 fn from(value: i32) -> Self {
458 Self::new(value)
459 }
460}
461
462impl From<DragToolId> for i32 {
463 #[inline]
464 fn from(value: DragToolId) -> Self {
465 value.raw()
466 }
467}
468
469impl fmt::Display for DragToolId {
470 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
471 self.0.fmt(f)
472 }
473}
474
475fn color4(rgba: [f32; 4]) -> sys::ImVec4_c {
476 sys::ImVec4_c {
477 x: rgba[0],
478 y: rgba[1],
479 z: rgba[2],
480 w: rgba[3],
481 }
482}
483
484impl PlotUi<'_> {
485 pub fn drag_point(
487 &self,
488 id: DragToolId,
489 x: &mut f64,
490 y: &mut f64,
491 color: [f32; 4],
492 size: f32,
493 flags: crate::DragToolFlags,
494 ) -> DragResult {
495 let mut clicked = false;
496 let mut hovered = false;
497 let mut held = false;
498 let _guard = self.bind();
499 let changed = unsafe {
500 sys::ImPlot_DragPoint(
501 id.raw(),
502 x as *mut f64,
503 y as *mut f64,
504 color4(color),
505 size,
506 flags.bits() as i32,
507 &mut clicked as *mut bool,
508 &mut hovered as *mut bool,
509 &mut held as *mut bool,
510 )
511 };
512 DragResult {
513 changed,
514 clicked,
515 hovered,
516 held,
517 }
518 }
519
520 pub fn drag_line_x(
522 &self,
523 id: DragToolId,
524 x: &mut f64,
525 color: [f32; 4],
526 thickness: f32,
527 flags: crate::DragToolFlags,
528 ) -> DragResult {
529 let mut clicked = false;
530 let mut hovered = false;
531 let mut held = false;
532 let _guard = self.bind();
533 let changed = unsafe {
534 sys::ImPlot_DragLineX(
535 id.raw(),
536 x as *mut f64,
537 color4(color),
538 thickness,
539 flags.bits() as i32,
540 &mut clicked as *mut bool,
541 &mut hovered as *mut bool,
542 &mut held as *mut bool,
543 )
544 };
545 DragResult {
546 changed,
547 clicked,
548 hovered,
549 held,
550 }
551 }
552
553 pub fn drag_line_y(
555 &self,
556 id: DragToolId,
557 y: &mut f64,
558 color: [f32; 4],
559 thickness: f32,
560 flags: crate::DragToolFlags,
561 ) -> DragResult {
562 let mut clicked = false;
563 let mut hovered = false;
564 let mut held = false;
565 let _guard = self.bind();
566 let changed = unsafe {
567 sys::ImPlot_DragLineY(
568 id.raw(),
569 y as *mut f64,
570 color4(color),
571 thickness,
572 flags.bits() as i32,
573 &mut clicked as *mut bool,
574 &mut hovered as *mut bool,
575 &mut held as *mut bool,
576 )
577 };
578 DragResult {
579 changed,
580 clicked,
581 hovered,
582 held,
583 }
584 }
585}
586
587#[cfg(test)]
588mod tests {
589 use super::DragToolId;
590
591 #[test]
592 fn drag_tool_id_round_trips_raw_values() {
593 let id = DragToolId::new(-7);
594 assert_eq!(id.raw(), -7);
595 assert_eq!(i32::from(id), -7);
596
597 let other = DragToolId::from(120482);
598 assert_eq!(other.raw(), 120482);
599 assert_eq!(other.to_string(), "120482");
600 }
601}