1use crate::{AxisFlags, PlotCond, XAxis, YAxis, sys};
2use dear_imgui_rs::{Context as ImGuiContext, Ui};
3use dear_imgui_sys as imgui_sys;
4
5pub struct PlotContext {
10 raw: *mut sys::ImPlotContext,
11}
12
13impl PlotContext {
14 pub fn try_create(_imgui_ctx: &ImGuiContext) -> dear_imgui_rs::ImGuiResult<Self> {
19 unsafe {
23 sys::ImPlot_SetImGuiContext(imgui_sys::igGetCurrentContext());
24 }
25
26 let raw = unsafe { sys::ImPlot_CreateContext() };
27 if raw.is_null() {
28 return Err(dear_imgui_rs::ImGuiError::context_creation(
29 "ImPlot_CreateContext returned null",
30 ));
31 }
32
33 unsafe {
35 sys::ImPlot_SetCurrentContext(raw);
36 }
37
38 Ok(Self { raw })
39 }
40
41 pub fn create(imgui_ctx: &ImGuiContext) -> Self {
43 Self::try_create(imgui_ctx).expect("Failed to create ImPlot context")
44 }
45
46 pub fn current() -> Option<Self> {
50 let raw = unsafe { sys::ImPlot_GetCurrentContext() };
51 if raw.is_null() {
52 None
53 } else {
54 Some(Self { raw })
55 }
56 }
57
58 pub fn set_as_current(&self) {
60 unsafe {
61 sys::ImPlot_SetCurrentContext(self.raw);
62 }
63 }
64
65 pub fn get_plot_ui<'ui>(&'ui self, ui: &'ui Ui) -> PlotUi<'ui> {
70 PlotUi { context: self, ui }
71 }
72
73 pub unsafe fn raw(&self) -> *mut sys::ImPlotContext {
80 self.raw
81 }
82}
83
84impl Drop for PlotContext {
85 fn drop(&mut self) {
86 if !self.raw.is_null() {
87 unsafe {
88 sys::ImPlot_DestroyContext(self.raw);
89 }
90 }
91 }
92}
93
94pub struct PlotUi<'ui> {
101 #[allow(dead_code)]
102 context: &'ui PlotContext,
103 #[allow(dead_code)]
104 ui: &'ui Ui,
105}
106
107impl<'ui> PlotUi<'ui> {
108 pub fn begin_plot(&self, title: &str) -> Option<PlotToken<'_>> {
113 let title_cstr = std::ffi::CString::new(title).ok()?;
114
115 let size = sys::ImVec2 { x: -1.0, y: 0.0 };
116 let started = unsafe { sys::ImPlot_BeginPlot(title_cstr.as_ptr(), size, 0) };
117
118 if started {
119 Some(PlotToken::new())
120 } else {
121 None
122 }
123 }
124
125 pub fn begin_plot_with_size(&self, title: &str, size: [f32; 2]) -> Option<PlotToken<'_>> {
127 let title_cstr = std::ffi::CString::new(title).ok()?;
128
129 let plot_size = sys::ImVec2 {
130 x: size[0],
131 y: size[1],
132 };
133 let started = unsafe { sys::ImPlot_BeginPlot(title_cstr.as_ptr(), plot_size, 0) };
134
135 if started {
136 Some(PlotToken::new())
137 } else {
138 None
139 }
140 }
141
142 pub fn plot_line(&self, label: &str, x_data: &[f64], y_data: &[f64]) {
146 if x_data.len() != y_data.len() {
147 return; }
149
150 let label_cstr = std::ffi::CString::new(label).unwrap_or_default();
151
152 unsafe {
153 sys::ImPlot_PlotLine_doublePtrdoublePtr(
154 label_cstr.as_ptr(),
155 x_data.as_ptr(),
156 y_data.as_ptr(),
157 x_data.len() as i32,
158 0,
159 0,
160 0,
161 );
162 }
163 }
164
165 pub fn plot_scatter(&self, label: &str, x_data: &[f64], y_data: &[f64]) {
167 if x_data.len() != y_data.len() {
168 return; }
170
171 let label_cstr = std::ffi::CString::new(label).unwrap_or_default();
172
173 unsafe {
174 sys::ImPlot_PlotScatter_doublePtrdoublePtr(
175 label_cstr.as_ptr(),
176 x_data.as_ptr(),
177 y_data.as_ptr(),
178 x_data.len() as i32,
179 0,
180 0,
181 0,
182 );
183 }
184 }
185
186 pub fn is_plot_hovered(&self) -> bool {
188 unsafe { sys::ImPlot_IsPlotHovered() }
189 }
190
191 pub fn get_plot_mouse_pos(&self, y_axis: Option<crate::YAxisChoice>) -> sys::ImPlotPoint {
193 let y_axis_i32 = crate::y_axis_choice_option_to_i32(y_axis);
194 let y_axis = match y_axis_i32 {
195 0 => 3,
196 1 => 4,
197 2 => 5,
198 _ => 3,
199 };
200 let mut out = sys::ImPlotPoint { x: 0.0, y: 0.0 };
201 unsafe {
202 sys::ImPlot_GetPlotMousePos(&mut out as *mut sys::ImPlotPoint, 0, y_axis);
203 }
204 out
205 }
206
207 pub fn get_plot_mouse_pos_axes(&self, x_axis: XAxis, y_axis: YAxis) -> sys::ImPlotPoint {
209 let mut out = sys::ImPlotPoint { x: 0.0, y: 0.0 };
210 unsafe {
211 sys::ImPlot_GetPlotMousePos(
212 &mut out as *mut sys::ImPlotPoint,
213 x_axis as i32,
214 y_axis as i32,
215 )
216 };
217 out
218 }
219
220 pub fn set_axes(&self, x_axis: XAxis, y_axis: YAxis) {
222 unsafe { sys::ImPlot_SetAxes(x_axis as i32, y_axis as i32) }
223 }
224
225 pub fn setup_x_axis(&self, axis: XAxis, label: Option<&str>, flags: AxisFlags) {
227 let label_cstr = label.and_then(|s| std::ffi::CString::new(s).ok());
228 let ptr = label_cstr
229 .as_ref()
230 .map(|c| c.as_ptr())
231 .unwrap_or(std::ptr::null());
232 unsafe {
233 sys::ImPlot_SetupAxis(
234 axis as sys::ImAxis,
235 ptr,
236 flags.bits() as sys::ImPlotAxisFlags,
237 )
238 }
239 }
240
241 pub fn setup_y_axis(&self, axis: YAxis, label: Option<&str>, flags: AxisFlags) {
243 let label_cstr = label.and_then(|s| std::ffi::CString::new(s).ok());
244 let ptr = label_cstr
245 .as_ref()
246 .map(|c| c.as_ptr())
247 .unwrap_or(std::ptr::null());
248 unsafe {
249 sys::ImPlot_SetupAxis(
250 axis as sys::ImAxis,
251 ptr,
252 flags.bits() as sys::ImPlotAxisFlags,
253 )
254 }
255 }
256
257 pub fn setup_x_axis_limits(&self, axis: XAxis, min: f64, max: f64, cond: PlotCond) {
259 unsafe {
260 sys::ImPlot_SetupAxisLimits(axis as sys::ImAxis, min, max, cond as sys::ImPlotCond)
261 }
262 }
263
264 pub fn setup_y_axis_limits(&self, axis: YAxis, min: f64, max: f64, cond: PlotCond) {
266 unsafe {
267 sys::ImPlot_SetupAxisLimits(axis as sys::ImAxis, min, max, cond as sys::ImPlotCond)
268 }
269 }
270
271 pub fn setup_axis_links(
273 &self,
274 axis: i32,
275 link_min: Option<&mut f64>,
276 link_max: Option<&mut f64>,
277 ) {
278 let pmin = link_min.map_or(std::ptr::null_mut(), |r| r as *mut f64);
279 let pmax = link_max.map_or(std::ptr::null_mut(), |r| r as *mut f64);
280 unsafe { sys::ImPlot_SetupAxisLinks(axis, pmin, pmax) }
281 }
282
283 pub fn setup_axes(
285 &self,
286 x_label: Option<&str>,
287 y_label: Option<&str>,
288 x_flags: AxisFlags,
289 y_flags: AxisFlags,
290 ) {
291 let x_c = x_label.and_then(|s| std::ffi::CString::new(s).ok());
292 let y_c = y_label.and_then(|s| std::ffi::CString::new(s).ok());
293 let xp = x_c.as_ref().map(|c| c.as_ptr()).unwrap_or(std::ptr::null());
294 let yp = y_c.as_ref().map(|c| c.as_ptr()).unwrap_or(std::ptr::null());
295 unsafe {
296 sys::ImPlot_SetupAxes(
297 xp,
298 yp,
299 x_flags.bits() as sys::ImPlotAxisFlags,
300 y_flags.bits() as sys::ImPlotAxisFlags,
301 )
302 }
303 }
304
305 pub fn setup_axes_limits(
307 &self,
308 x_min: f64,
309 x_max: f64,
310 y_min: f64,
311 y_max: f64,
312 cond: PlotCond,
313 ) {
314 unsafe { sys::ImPlot_SetupAxesLimits(x_min, x_max, y_min, y_max, cond as sys::ImPlotCond) }
315 }
316
317 pub fn setup_finish(&self) {
319 unsafe { sys::ImPlot_SetupFinish() }
320 }
321
322 pub fn set_next_x_axis_limits(&self, axis: XAxis, min: f64, max: f64, cond: PlotCond) {
324 unsafe {
325 sys::ImPlot_SetNextAxisLimits(axis as sys::ImAxis, min, max, cond as sys::ImPlotCond)
326 }
327 }
328
329 pub fn set_next_y_axis_limits(&self, axis: YAxis, min: f64, max: f64, cond: PlotCond) {
331 unsafe {
332 sys::ImPlot_SetNextAxisLimits(axis as sys::ImAxis, min, max, cond as sys::ImPlotCond)
333 }
334 }
335
336 pub fn set_next_axis_links(
338 &self,
339 axis: i32,
340 link_min: Option<&mut f64>,
341 link_max: Option<&mut f64>,
342 ) {
343 let pmin = link_min.map_or(std::ptr::null_mut(), |r| r as *mut f64);
344 let pmax = link_max.map_or(std::ptr::null_mut(), |r| r as *mut f64);
345 unsafe { sys::ImPlot_SetNextAxisLinks(axis, pmin, pmax) }
346 }
347
348 pub fn set_next_axes_limits(
350 &self,
351 x_min: f64,
352 x_max: f64,
353 y_min: f64,
354 y_max: f64,
355 cond: PlotCond,
356 ) {
357 unsafe {
358 sys::ImPlot_SetNextAxesLimits(x_min, x_max, y_min, y_max, cond as sys::ImPlotCond)
359 }
360 }
361
362 pub fn set_next_axes_to_fit(&self) {
364 unsafe { sys::ImPlot_SetNextAxesToFit() }
365 }
366
367 pub fn set_next_axis_to_fit(&self, axis: i32) {
369 unsafe { sys::ImPlot_SetNextAxisToFit(axis as sys::ImAxis) }
370 }
371
372 pub fn set_next_x_axis_to_fit(&self, axis: XAxis) {
374 unsafe { sys::ImPlot_SetNextAxisToFit(axis as sys::ImAxis) }
375 }
376
377 pub fn set_next_y_axis_to_fit(&self, axis: YAxis) {
379 unsafe { sys::ImPlot_SetNextAxisToFit(axis as sys::ImAxis) }
380 }
381
382 pub fn setup_x_axis_ticks_positions(
384 &self,
385 axis: XAxis,
386 values: &[f64],
387 labels: Option<&[&str]>,
388 keep_default: bool,
389 ) {
390 let cstrs: Option<Vec<std::ffi::CString>> = labels.map(|ls| {
391 ls.iter()
392 .map(|&s| std::ffi::CString::new(s).unwrap_or_default())
393 .collect()
394 });
395 if let Some(vec) = cstrs {
397 let raw: Vec<*const i8> = vec.iter().map(|c| c.as_ptr()).collect();
398 unsafe {
399 sys::ImPlot_SetupAxisTicks_doublePtr(
400 axis as sys::ImAxis,
401 values.as_ptr(),
402 values.len() as i32,
403 raw.as_ptr(),
404 keep_default,
405 )
406 }
407 } else {
408 unsafe {
409 sys::ImPlot_SetupAxisTicks_doublePtr(
410 axis as sys::ImAxis,
411 values.as_ptr(),
412 values.len() as i32,
413 std::ptr::null(),
414 keep_default,
415 )
416 }
417 }
418 }
419
420 pub fn setup_y_axis_ticks_positions(
422 &self,
423 axis: YAxis,
424 values: &[f64],
425 labels: Option<&[&str]>,
426 keep_default: bool,
427 ) {
428 let cstrs: Option<Vec<std::ffi::CString>> = labels.map(|ls| {
429 ls.iter()
430 .map(|&s| std::ffi::CString::new(s).unwrap_or_default())
431 .collect()
432 });
433 if let Some(cstrs) = cstrs {
434 let raw: Vec<*const i8> = cstrs.iter().map(|c| c.as_ptr()).collect();
435 unsafe {
436 sys::ImPlot_SetupAxisTicks_doublePtr(
437 axis as sys::ImAxis,
438 values.as_ptr(),
439 values.len() as i32,
440 raw.as_ptr(),
441 keep_default,
442 )
443 }
444 } else {
445 unsafe {
446 sys::ImPlot_SetupAxisTicks_doublePtr(
447 axis as sys::ImAxis,
448 values.as_ptr(),
449 values.len() as i32,
450 std::ptr::null(),
451 keep_default,
452 )
453 }
454 }
455 }
456
457 pub fn setup_x_axis_ticks_range(
459 &self,
460 axis: XAxis,
461 v_min: f64,
462 v_max: f64,
463 n_ticks: i32,
464 labels: Option<&[&str]>,
465 keep_default: bool,
466 ) {
467 let cstrs: Option<Vec<std::ffi::CString>> = labels.map(|ls| {
468 ls.iter()
469 .map(|&s| std::ffi::CString::new(s).unwrap_or_default())
470 .collect()
471 });
472 if let Some(cstrs) = cstrs {
473 let raw: Vec<*const i8> = cstrs.iter().map(|c| c.as_ptr()).collect();
474 unsafe {
475 sys::ImPlot_SetupAxisTicks_double(
476 axis as sys::ImAxis,
477 v_min,
478 v_max,
479 n_ticks,
480 raw.as_ptr(),
481 keep_default,
482 )
483 }
484 } else {
485 unsafe {
486 sys::ImPlot_SetupAxisTicks_double(
487 axis as sys::ImAxis,
488 v_min,
489 v_max,
490 n_ticks,
491 std::ptr::null(),
492 keep_default,
493 )
494 }
495 }
496 }
497
498 pub fn setup_y_axis_ticks_range(
500 &self,
501 axis: YAxis,
502 v_min: f64,
503 v_max: f64,
504 n_ticks: i32,
505 labels: Option<&[&str]>,
506 keep_default: bool,
507 ) {
508 let cstrs: Option<Vec<std::ffi::CString>> = labels.map(|ls| {
509 ls.iter()
510 .map(|&s| std::ffi::CString::new(s).unwrap_or_default())
511 .collect()
512 });
513 if let Some(cstrs) = cstrs {
514 let raw: Vec<*const i8> = cstrs.iter().map(|c| c.as_ptr()).collect();
515 unsafe {
516 sys::ImPlot_SetupAxisTicks_double(
517 axis as sys::ImAxis,
518 v_min,
519 v_max,
520 n_ticks,
521 raw.as_ptr(),
522 keep_default,
523 )
524 }
525 } else {
526 unsafe {
527 sys::ImPlot_SetupAxisTicks_double(
528 axis as sys::ImAxis,
529 v_min,
530 v_max,
531 n_ticks,
532 std::ptr::null(),
533 keep_default,
534 )
535 }
536 }
537 }
538
539 pub fn setup_x_axis_format(&self, axis: XAxis, fmt: &str) {
541 if let Ok(c) = std::ffi::CString::new(fmt) {
542 unsafe { sys::ImPlot_SetupAxisFormat_Str(axis as sys::ImAxis, c.as_ptr()) }
543 }
544 }
545
546 pub fn setup_y_axis_format(&self, axis: YAxis, fmt: &str) {
548 if let Ok(c) = std::ffi::CString::new(fmt) {
549 unsafe { sys::ImPlot_SetupAxisFormat_Str(axis as sys::ImAxis, c.as_ptr()) }
550 }
551 }
552
553 pub fn setup_x_axis_scale(&self, axis: XAxis, scale: sys::ImPlotScale) {
555 unsafe { sys::ImPlot_SetupAxisScale_PlotScale(axis as sys::ImAxis, scale) }
556 }
557
558 pub fn setup_y_axis_scale(&self, axis: YAxis, scale: sys::ImPlotScale) {
560 unsafe { sys::ImPlot_SetupAxisScale_PlotScale(axis as sys::ImAxis, scale) }
561 }
562
563 pub fn setup_axis_limits_constraints(&self, axis: i32, v_min: f64, v_max: f64) {
565 unsafe { sys::ImPlot_SetupAxisLimitsConstraints(axis as sys::ImAxis, v_min, v_max) }
566 }
567
568 pub fn setup_axis_zoom_constraints(&self, axis: i32, z_min: f64, z_max: f64) {
570 unsafe { sys::ImPlot_SetupAxisZoomConstraints(axis as sys::ImAxis, z_min, z_max) }
571 }
572
573 pub fn setup_x_axis_format_closure<F>(&self, axis: XAxis, f: F) -> AxisFormatterToken
576 where
577 F: Fn(f64) -> String + Send + Sync + 'static,
578 {
579 AxisFormatterToken::new(axis as sys::ImAxis, f)
580 }
581
582 pub fn setup_y_axis_format_closure<F>(&self, axis: YAxis, f: F) -> AxisFormatterToken
584 where
585 F: Fn(f64) -> String + Send + Sync + 'static,
586 {
587 AxisFormatterToken::new(axis as sys::ImAxis, f)
588 }
589
590 pub fn setup_x_axis_transform_closure<FW, INV>(
593 &self,
594 axis: XAxis,
595 forward: FW,
596 inverse: INV,
597 ) -> AxisTransformToken
598 where
599 FW: Fn(f64) -> f64 + Send + Sync + 'static,
600 INV: Fn(f64) -> f64 + Send + Sync + 'static,
601 {
602 AxisTransformToken::new(axis as sys::ImAxis, forward, inverse)
603 }
604
605 pub fn setup_y_axis_transform_closure<FW, INV>(
607 &self,
608 axis: YAxis,
609 forward: FW,
610 inverse: INV,
611 ) -> AxisTransformToken
612 where
613 FW: Fn(f64) -> f64 + Send + Sync + 'static,
614 INV: Fn(f64) -> f64 + Send + Sync + 'static,
615 {
616 AxisTransformToken::new(axis as sys::ImAxis, forward, inverse)
617 }
618}
619
620struct FormatterHolder {
623 func: Box<dyn Fn(f64) -> String + Send + Sync + 'static>,
624}
625
626pub struct AxisFormatterToken {
627 holder: Box<FormatterHolder>,
628 axis: sys::ImAxis,
629}
630
631impl AxisFormatterToken {
632 fn new<F>(axis: sys::ImAxis, f: F) -> Self
633 where
634 F: Fn(f64) -> String + Send + Sync + 'static,
635 {
636 let holder = Box::new(FormatterHolder { func: Box::new(f) });
637 let user = &*holder as *const FormatterHolder as *mut std::os::raw::c_void;
638 unsafe {
639 sys::ImPlot_SetupAxisFormat_PlotFormatter(
640 axis as sys::ImAxis,
641 Some(formatter_thunk),
642 user,
643 )
644 }
645 Self { holder, axis }
646 }
647}
648
649impl Drop for AxisFormatterToken {
650 fn drop(&mut self) {
651 }
653}
654
655unsafe extern "C" fn formatter_thunk(
656 value: f64,
657 buff: *mut std::os::raw::c_char,
658 size: std::os::raw::c_int,
659 user_data: *mut std::os::raw::c_void,
660) -> std::os::raw::c_int {
661 if user_data.is_null() || buff.is_null() || size <= 0 {
662 return 0;
663 }
664 let holder = unsafe { &*(user_data as *const FormatterHolder) };
665 let s = (holder.func)(value);
666 let bytes = s.as_bytes();
667 let max = (size - 1).max(0) as usize;
668 let n = bytes.len().min(max);
669 std::ptr::copy_nonoverlapping(bytes.as_ptr(), buff as *mut u8, n);
670 *buff.add(n) = 0;
671 n as std::os::raw::c_int
672}
673
674struct TransformHolder {
677 forward: Box<dyn Fn(f64) -> f64 + Send + Sync + 'static>,
678 inverse: Box<dyn Fn(f64) -> f64 + Send + Sync + 'static>,
679}
680
681pub struct AxisTransformToken {
682 holder: Box<TransformHolder>,
683 axis: sys::ImAxis,
684}
685
686impl AxisTransformToken {
687 fn new<FW, INV>(axis: sys::ImAxis, forward: FW, inverse: INV) -> Self
688 where
689 FW: Fn(f64) -> f64 + Send + Sync + 'static,
690 INV: Fn(f64) -> f64 + Send + Sync + 'static,
691 {
692 let holder = Box::new(TransformHolder {
693 forward: Box::new(forward),
694 inverse: Box::new(inverse),
695 });
696 let user = &*holder as *const TransformHolder as *mut std::os::raw::c_void;
697 unsafe {
698 sys::ImPlot_SetupAxisScale_PlotTransform(
699 axis as sys::ImAxis,
700 Some(transform_forward_thunk),
701 Some(transform_inverse_thunk),
702 user,
703 )
704 }
705 Self { holder, axis }
706 }
707}
708
709impl Drop for AxisTransformToken {
710 fn drop(&mut self) {
711 }
713}
714
715unsafe extern "C" fn transform_forward_thunk(
716 value: f64,
717 user_data: *mut std::os::raw::c_void,
718) -> f64 {
719 let holder = unsafe { &*(user_data as *const TransformHolder) };
720 (holder.forward)(value)
721}
722
723unsafe extern "C" fn transform_inverse_thunk(
724 value: f64,
725 user_data: *mut std::os::raw::c_void,
726) -> f64 {
727 let holder = unsafe { &*(user_data as *const TransformHolder) };
728 (holder.inverse)(value)
729}
730
731pub struct PlotToken<'ui> {
735 _lifetime: std::marker::PhantomData<&'ui ()>,
736}
737
738impl<'ui> PlotToken<'ui> {
739 pub(crate) fn new() -> Self {
741 Self {
742 _lifetime: std::marker::PhantomData,
743 }
744 }
745
746 pub fn end(self) {
751 }
753}
754
755impl<'ui> Drop for PlotToken<'ui> {
756 fn drop(&mut self) {
757 unsafe {
758 sys::ImPlot_EndPlot();
759 }
760 }
761}