1use crate::{Axis, XAxis, YAxis, compat_ffi, sys};
4use dear_imgui_rs::with_scratch_txt;
5use std::fmt;
6
7fn assert_finite_f32(caller: &str, name: &str, value: f32) {
8 assert!(value.is_finite(), "{caller} {name} must be finite");
9}
10
11fn assert_finite_f64(caller: &str, name: &str, value: f64) {
12 assert!(value.is_finite(), "{caller} {name} must be finite");
13}
14
15fn assert_finite_vec2(caller: &str, name: &str, value: [f32; 2]) {
16 assert!(
17 value[0].is_finite() && value[1].is_finite(),
18 "{caller} {name} must be finite"
19 );
20}
21
22fn assert_finite_color(caller: &str, name: &str, value: [f32; 4]) {
23 assert!(
24 value.iter().all(|component| component.is_finite()),
25 "{caller} {name} must be finite"
26 );
27}
28
29fn assert_finite_point(caller: &str, name: &str, value: sys::ImPlotPoint) {
30 assert!(
31 value.x.is_finite() && value.y.is_finite(),
32 "{caller} {name} must be finite"
33 );
34}
35
36pub fn is_plot_hovered() -> bool {
38 unsafe { sys::ImPlot_IsPlotHovered() }
39}
40
41pub fn is_subplots_hovered() -> bool {
43 unsafe { sys::ImPlot_IsSubplotsHovered() }
44}
45
46pub fn is_legend_entry_hovered(label: &str) -> bool {
48 let label = if label.contains('\0') { "" } else { label };
49 with_scratch_txt(label, |ptr| unsafe {
50 sys::ImPlot_IsLegendEntryHovered(ptr)
51 })
52}
53
54pub fn get_plot_mouse_position(y_axis_choice: Option<crate::YAxisChoice>) -> sys::ImPlotPoint {
56 let x_axis = 0; let y_axis = match y_axis_choice {
58 Some(crate::YAxisChoice::First) => 3, Some(crate::YAxisChoice::Second) => 4, Some(crate::YAxisChoice::Third) => 5, None => 3, };
63 unsafe { sys::ImPlot_GetPlotMousePos(x_axis as sys::ImAxis, y_axis as sys::ImAxis) }
64}
65
66pub fn get_plot_mouse_position_axes(x_axis: XAxis, y_axis: YAxis) -> sys::ImPlotPoint {
68 unsafe { sys::ImPlot_GetPlotMousePos(x_axis as sys::ImAxis, y_axis as sys::ImAxis) }
69}
70
71pub fn pixels_to_plot(
73 pixel_position: [f32; 2],
74 y_axis_choice: Option<crate::YAxisChoice>,
75) -> sys::ImPlotPoint {
76 assert_finite_vec2("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 unsafe {
85 let plot = sys::ImPlot_GetCurrentPlot();
86 if plot.is_null() {
87 return sys::ImPlotPoint { x: 0.0, y: 0.0 };
88 }
89 let x_axis_ptr = sys::ImPlotPlot_XAxis_Nil(plot, 0);
90 let y_axis_ptr = sys::ImPlotPlot_YAxis_Nil(plot, y_index);
91 let x = sys::ImPlotAxis_PixelsToPlot(x_axis_ptr, pixel_position[0]);
92 let y = sys::ImPlotAxis_PixelsToPlot(y_axis_ptr, pixel_position[1]);
93 sys::ImPlotPoint { x, y }
94 }
95}
96
97pub fn pixels_to_plot_axes(
99 pixel_position: [f32; 2],
100 x_axis: XAxis,
101 y_axis: YAxis,
102) -> sys::ImPlotPoint {
103 assert_finite_vec2("pixels_to_plot_axes()", "pixel_position", pixel_position);
104 unsafe {
105 let plot = sys::ImPlot_GetCurrentPlot();
106 if plot.is_null() {
107 return sys::ImPlotPoint { x: 0.0, y: 0.0 };
108 }
109 let x_axis_ptr = sys::ImPlotPlot_XAxis_Nil(plot, x_axis as i32);
110 let y_axis_ptr = sys::ImPlotPlot_YAxis_Nil(plot, y_axis.to_index());
111 let x = sys::ImPlotAxis_PixelsToPlot(x_axis_ptr, pixel_position[0]);
112 let y = sys::ImPlotAxis_PixelsToPlot(y_axis_ptr, pixel_position[1]);
113 sys::ImPlotPoint { x, y }
114 }
115}
116
117pub fn plot_to_pixels(
119 plot_position: sys::ImPlotPoint,
120 y_axis_choice: Option<crate::YAxisChoice>,
121) -> [f32; 2] {
122 assert_finite_point("plot_to_pixels()", "plot_position", plot_position);
123 let y_index = match y_axis_choice {
124 Some(crate::YAxisChoice::First) => 0,
125 Some(crate::YAxisChoice::Second) => 1,
126 Some(crate::YAxisChoice::Third) => 2,
127 None => 0,
128 };
129 unsafe {
130 let plot = sys::ImPlot_GetCurrentPlot();
131 if plot.is_null() {
132 return [0.0, 0.0];
133 }
134 let x_axis_ptr = sys::ImPlotPlot_XAxis_Nil(plot, 0);
135 let y_axis_ptr = sys::ImPlotPlot_YAxis_Nil(plot, y_index);
136 let px = sys::ImPlotAxis_PlotToPixels(x_axis_ptr, plot_position.x);
137 let py = sys::ImPlotAxis_PlotToPixels(y_axis_ptr, plot_position.y);
138 [px, py]
139 }
140}
141
142pub fn plot_to_pixels_axes(
144 plot_position: sys::ImPlotPoint,
145 x_axis: XAxis,
146 y_axis: YAxis,
147) -> [f32; 2] {
148 assert_finite_point("plot_to_pixels_axes()", "plot_position", plot_position);
149 unsafe {
150 let plot = sys::ImPlot_GetCurrentPlot();
151 if plot.is_null() {
152 return [0.0, 0.0];
153 }
154 let x_axis_ptr = sys::ImPlotPlot_XAxis_Nil(plot, x_axis as i32);
155 let y_axis_ptr = sys::ImPlotPlot_YAxis_Nil(plot, y_axis.to_index());
156 let px = sys::ImPlotAxis_PlotToPixels(x_axis_ptr, plot_position.x);
157 let py = sys::ImPlotAxis_PlotToPixels(y_axis_ptr, plot_position.y);
158 [px, py]
159 }
160}
161
162pub fn get_plot_limits(
164 _x_axis_choice: Option<crate::YAxisChoice>,
165 y_axis_choice: Option<crate::YAxisChoice>,
166) -> sys::ImPlotRect {
167 let x_axis = 0; let y_axis = match y_axis_choice {
169 Some(crate::YAxisChoice::First) => 3, Some(crate::YAxisChoice::Second) => 4, Some(crate::YAxisChoice::Third) => 5, None => 3, };
174 unsafe { sys::ImPlot_GetPlotLimits(x_axis, y_axis) }
175}
176
177pub fn is_plot_selected() -> bool {
179 unsafe { sys::ImPlot_IsPlotSelected() }
180}
181
182pub fn get_plot_selection_axes(x_axis: XAxis, y_axis: YAxis) -> Option<sys::ImPlotRect> {
184 if !is_plot_selected() {
185 return None;
186 }
187 let rect = unsafe { sys::ImPlot_GetPlotSelection(x_axis as i32, y_axis as i32) };
188 Some(rect)
189}
190
191pub fn annotation_point(
193 x: f64,
194 y: f64,
195 color: [f32; 4],
196 pixel_offset: [f32; 2],
197 clamp: bool,
198 round: bool,
199) {
200 assert_finite_f64("annotation_point()", "x", x);
201 assert_finite_f64("annotation_point()", "y", y);
202 assert_finite_color("annotation_point()", "color", color);
203 assert_finite_vec2("annotation_point()", "pixel_offset", pixel_offset);
204 let col = sys::ImVec4_c {
205 x: color[0],
206 y: color[1],
207 z: color[2],
208 w: color[3],
209 };
210 let off = sys::ImVec2_c {
211 x: pixel_offset[0],
212 y: pixel_offset[1],
213 };
214 unsafe { sys::ImPlot_Annotation_Bool(x, y, col, off, clamp, round) }
215}
216
217pub fn annotation_text(
222 x: f64,
223 y: f64,
224 color: [f32; 4],
225 pixel_offset: [f32; 2],
226 clamp: bool,
227 text: &str,
228) {
229 assert_finite_f64("annotation_text()", "x", x);
230 assert_finite_f64("annotation_text()", "y", y);
231 assert_finite_color("annotation_text()", "color", color);
232 assert_finite_vec2("annotation_text()", "pixel_offset", pixel_offset);
233 let col = sys::ImVec4_c {
234 x: color[0],
235 y: color[1],
236 z: color[2],
237 w: color[3],
238 };
239 let off = sys::ImVec2_c {
240 x: pixel_offset[0],
241 y: pixel_offset[1],
242 };
243 assert!(!text.contains('\0'), "text contained NUL");
244 with_scratch_txt(text, |ptr| unsafe {
245 compat_ffi::ImPlot_Annotation_Str0(x, y, col, off, clamp, ptr)
246 })
247}
248
249pub fn tag_x(x: f64, color: [f32; 4], round: bool) {
251 assert_finite_f64("tag_x()", "x", x);
252 assert_finite_color("tag_x()", "color", color);
253 let col = sys::ImVec4_c {
254 x: color[0],
255 y: color[1],
256 z: color[2],
257 w: color[3],
258 };
259 unsafe { sys::ImPlot_TagX_Bool(x, col, round) }
260}
261
262pub fn tag_x_text(x: f64, color: [f32; 4], text: &str) {
264 assert_finite_f64("tag_x_text()", "x", x);
265 assert_finite_color("tag_x_text()", "color", color);
266 let col = sys::ImVec4_c {
267 x: color[0],
268 y: color[1],
269 z: color[2],
270 w: color[3],
271 };
272 assert!(!text.contains('\0'), "text contained NUL");
273 with_scratch_txt(text, |ptr| unsafe {
274 compat_ffi::ImPlot_TagX_Str0(x, col, ptr)
275 })
276}
277
278pub fn tag_y(y: f64, color: [f32; 4], round: bool) {
280 assert_finite_f64("tag_y()", "y", y);
281 assert_finite_color("tag_y()", "color", color);
282 let col = sys::ImVec4_c {
283 x: color[0],
284 y: color[1],
285 z: color[2],
286 w: color[3],
287 };
288 unsafe { sys::ImPlot_TagY_Bool(y, col, round) }
289}
290
291pub fn tag_y_text(y: f64, color: [f32; 4], text: &str) {
293 assert_finite_f64("tag_y_text()", "y", y);
294 assert_finite_color("tag_y_text()", "color", color);
295 let col = sys::ImVec4_c {
296 x: color[0],
297 y: color[1],
298 z: color[2],
299 w: color[3],
300 };
301 assert!(!text.contains('\0'), "text contained NUL");
302 with_scratch_txt(text, |ptr| unsafe {
303 compat_ffi::ImPlot_TagY_Str0(y, col, ptr)
304 })
305}
306
307pub fn get_plot_limits_axes(x_axis: XAxis, y_axis: YAxis) -> sys::ImPlotRect {
309 unsafe { sys::ImPlot_GetPlotLimits(x_axis as i32, y_axis as i32) }
310}
311
312pub fn is_axis_hovered(axis: Axis) -> bool {
314 unsafe { sys::ImPlot_IsAxisHovered(axis.to_sys()) }
315}
316
317pub unsafe fn is_axis_hovered_unchecked(axis: sys::ImAxis) -> bool {
324 unsafe { sys::ImPlot_IsAxisHovered(axis) }
325}
326
327pub fn is_plot_x_axis_hovered() -> bool {
329 is_axis_hovered(Axis::X1)
330}
331
332pub fn is_plot_x_axis_hovered_axis(x_axis: XAxis) -> bool {
334 is_axis_hovered(x_axis.into())
335}
336
337pub fn is_plot_y_axis_hovered(y_axis_choice: Option<crate::YAxisChoice>) -> bool {
339 let y_axis = match y_axis_choice {
340 Some(crate::YAxisChoice::First) => 3, Some(crate::YAxisChoice::Second) => 4, Some(crate::YAxisChoice::Third) => 5, None => 3, };
345 is_axis_hovered(match y_axis {
346 3 => Axis::Y1,
347 4 => Axis::Y2,
348 5 => Axis::Y3,
349 _ => Axis::Y1,
350 })
351}
352
353pub fn is_plot_y_axis_hovered_axis(y_axis: YAxis) -> bool {
355 is_axis_hovered(y_axis.into())
356}
357
358#[cfg(feature = "demo")]
360pub fn show_demo_window(show: &mut bool) {
361 unsafe { sys::ImPlot_ShowDemoWindow(show) }
362}
363
364#[cfg(not(feature = "demo"))]
366pub fn show_demo_window(_show: &mut bool) {}
367
368pub fn show_user_guide() {
370 unsafe { sys::ImPlot_ShowUserGuide() }
371}
372
373pub fn show_metrics_window(open: &mut bool) {
375 unsafe { sys::ImPlot_ShowMetricsWindow(open as *mut bool) }
376}
377
378pub fn get_plot_pos() -> [f32; 2] {
380 let out = unsafe { crate::compat_ffi::ImPlot_GetPlotPos() };
381 [out.x, out.y]
382}
383
384pub fn get_plot_size() -> [f32; 2] {
386 let out = unsafe { crate::compat_ffi::ImPlot_GetPlotSize() };
387 [out.x, out.y]
388}
389
390pub fn get_plot_draw_list() -> *mut sys::ImDrawList {
392 unsafe { sys::ImPlot_GetPlotDrawList() }
393}
394
395pub fn push_plot_clip_rect(expand: f32) {
397 assert_finite_f32("push_plot_clip_rect()", "expand", expand);
398 unsafe { sys::ImPlot_PushPlotClipRect(expand) }
399}
400
401pub fn pop_plot_clip_rect() {
403 unsafe { sys::ImPlot_PopPlotClipRect() }
404}
405
406#[derive(Debug, Clone, Copy, Default)]
408pub struct DragResult {
409 pub changed: bool,
411 pub clicked: bool,
413 pub hovered: bool,
415 pub held: bool,
417}
418
419#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
424#[repr(transparent)]
425pub struct DragToolId(i32);
426
427impl DragToolId {
428 #[inline]
430 pub const fn new(id: i32) -> Self {
431 Self(id)
432 }
433
434 #[inline]
436 pub const fn raw(self) -> i32 {
437 self.0
438 }
439}
440
441impl From<i32> for DragToolId {
442 #[inline]
443 fn from(value: i32) -> Self {
444 Self::new(value)
445 }
446}
447
448impl From<DragToolId> for i32 {
449 #[inline]
450 fn from(value: DragToolId) -> Self {
451 value.raw()
452 }
453}
454
455impl fmt::Display for DragToolId {
456 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
457 self.0.fmt(f)
458 }
459}
460
461fn color4(rgba: [f32; 4]) -> sys::ImVec4_c {
462 sys::ImVec4_c {
463 x: rgba[0],
464 y: rgba[1],
465 z: rgba[2],
466 w: rgba[3],
467 }
468}
469
470pub fn drag_point(
472 id: DragToolId,
473 x: &mut f64,
474 y: &mut f64,
475 color: [f32; 4],
476 size: f32,
477 flags: crate::DragToolFlags,
478) -> DragResult {
479 let mut clicked = false;
480 let mut hovered = false;
481 let mut held = false;
482 let changed = unsafe {
483 sys::ImPlot_DragPoint(
484 id.raw(),
485 x as *mut f64,
486 y as *mut f64,
487 color4(color),
488 size,
489 flags.bits() as i32,
490 &mut clicked as *mut bool,
491 &mut hovered as *mut bool,
492 &mut held as *mut bool,
493 )
494 };
495 DragResult {
496 changed,
497 clicked,
498 hovered,
499 held,
500 }
501}
502
503pub fn drag_line_x(
505 id: DragToolId,
506 x: &mut f64,
507 color: [f32; 4],
508 thickness: f32,
509 flags: crate::DragToolFlags,
510) -> DragResult {
511 let mut clicked = false;
512 let mut hovered = false;
513 let mut held = false;
514 let changed = unsafe {
515 sys::ImPlot_DragLineX(
516 id.raw(),
517 x as *mut f64,
518 color4(color),
519 thickness,
520 flags.bits() as i32,
521 &mut clicked as *mut bool,
522 &mut hovered as *mut bool,
523 &mut held as *mut bool,
524 )
525 };
526 DragResult {
527 changed,
528 clicked,
529 hovered,
530 held,
531 }
532}
533
534pub fn drag_line_y(
536 id: DragToolId,
537 y: &mut f64,
538 color: [f32; 4],
539 thickness: f32,
540 flags: crate::DragToolFlags,
541) -> DragResult {
542 let mut clicked = false;
543 let mut hovered = false;
544 let mut held = false;
545 let changed = unsafe {
546 sys::ImPlot_DragLineY(
547 id.raw(),
548 y as *mut f64,
549 color4(color),
550 thickness,
551 flags.bits() as i32,
552 &mut clicked as *mut bool,
553 &mut hovered as *mut bool,
554 &mut held as *mut bool,
555 )
556 };
557 DragResult {
558 changed,
559 clicked,
560 hovered,
561 held,
562 }
563}
564
565#[cfg(test)]
566mod tests {
567 use super::DragToolId;
568
569 #[test]
570 fn drag_tool_id_round_trips_raw_values() {
571 let id = DragToolId::new(-7);
572 assert_eq!(id.raw(), -7);
573 assert_eq!(i32::from(id), -7);
574
575 let other = DragToolId::from(120482);
576 assert_eq!(other.raw(), 120482);
577 assert_eq!(other.to_string(), "120482");
578 }
579}