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_c { 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_c {
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 unsafe { sys::ImPlot_GetPlotMousePos(0, y_axis) }
201 }
202
203 pub fn get_plot_mouse_pos_axes(&self, x_axis: XAxis, y_axis: YAxis) -> sys::ImPlotPoint {
205 unsafe { sys::ImPlot_GetPlotMousePos(x_axis as i32, y_axis as i32) }
206 }
207
208 pub fn set_axes(&self, x_axis: XAxis, y_axis: YAxis) {
210 unsafe { sys::ImPlot_SetAxes(x_axis as i32, y_axis as i32) }
211 }
212
213 pub fn setup_x_axis(&self, axis: XAxis, label: Option<&str>, flags: AxisFlags) {
215 let label_cstr = label.and_then(|s| std::ffi::CString::new(s).ok());
216 let ptr = label_cstr
217 .as_ref()
218 .map(|c| c.as_ptr())
219 .unwrap_or(std::ptr::null());
220 unsafe {
221 sys::ImPlot_SetupAxis(
222 axis as sys::ImAxis,
223 ptr,
224 flags.bits() as sys::ImPlotAxisFlags,
225 )
226 }
227 }
228
229 pub fn setup_y_axis(&self, axis: YAxis, label: Option<&str>, flags: AxisFlags) {
231 let label_cstr = label.and_then(|s| std::ffi::CString::new(s).ok());
232 let ptr = label_cstr
233 .as_ref()
234 .map(|c| c.as_ptr())
235 .unwrap_or(std::ptr::null());
236 unsafe {
237 sys::ImPlot_SetupAxis(
238 axis as sys::ImAxis,
239 ptr,
240 flags.bits() as sys::ImPlotAxisFlags,
241 )
242 }
243 }
244
245 pub fn setup_x_axis_limits(&self, axis: XAxis, min: f64, max: f64, cond: PlotCond) {
247 unsafe {
248 sys::ImPlot_SetupAxisLimits(axis as sys::ImAxis, min, max, cond as sys::ImPlotCond)
249 }
250 }
251
252 pub fn setup_y_axis_limits(&self, axis: YAxis, min: f64, max: f64, cond: PlotCond) {
254 unsafe {
255 sys::ImPlot_SetupAxisLimits(axis as sys::ImAxis, min, max, cond as sys::ImPlotCond)
256 }
257 }
258
259 pub fn setup_axis_links(
261 &self,
262 axis: i32,
263 link_min: Option<&mut f64>,
264 link_max: Option<&mut f64>,
265 ) {
266 let pmin = link_min.map_or(std::ptr::null_mut(), |r| r as *mut f64);
267 let pmax = link_max.map_or(std::ptr::null_mut(), |r| r as *mut f64);
268 unsafe { sys::ImPlot_SetupAxisLinks(axis, pmin, pmax) }
269 }
270
271 pub fn setup_axes(
273 &self,
274 x_label: Option<&str>,
275 y_label: Option<&str>,
276 x_flags: AxisFlags,
277 y_flags: AxisFlags,
278 ) {
279 let x_c = x_label.and_then(|s| std::ffi::CString::new(s).ok());
280 let y_c = y_label.and_then(|s| std::ffi::CString::new(s).ok());
281 let xp = x_c.as_ref().map(|c| c.as_ptr()).unwrap_or(std::ptr::null());
282 let yp = y_c.as_ref().map(|c| c.as_ptr()).unwrap_or(std::ptr::null());
283 unsafe {
284 sys::ImPlot_SetupAxes(
285 xp,
286 yp,
287 x_flags.bits() as sys::ImPlotAxisFlags,
288 y_flags.bits() as sys::ImPlotAxisFlags,
289 )
290 }
291 }
292
293 pub fn setup_axes_limits(
295 &self,
296 x_min: f64,
297 x_max: f64,
298 y_min: f64,
299 y_max: f64,
300 cond: PlotCond,
301 ) {
302 unsafe { sys::ImPlot_SetupAxesLimits(x_min, x_max, y_min, y_max, cond as sys::ImPlotCond) }
303 }
304
305 pub fn setup_finish(&self) {
307 unsafe { sys::ImPlot_SetupFinish() }
308 }
309
310 pub fn set_next_x_axis_limits(&self, axis: XAxis, min: f64, max: f64, cond: PlotCond) {
312 unsafe {
313 sys::ImPlot_SetNextAxisLimits(axis as sys::ImAxis, min, max, cond as sys::ImPlotCond)
314 }
315 }
316
317 pub fn set_next_y_axis_limits(&self, axis: YAxis, min: f64, max: f64, cond: PlotCond) {
319 unsafe {
320 sys::ImPlot_SetNextAxisLimits(axis as sys::ImAxis, min, max, cond as sys::ImPlotCond)
321 }
322 }
323
324 pub fn set_next_axis_links(
326 &self,
327 axis: i32,
328 link_min: Option<&mut f64>,
329 link_max: Option<&mut f64>,
330 ) {
331 let pmin = link_min.map_or(std::ptr::null_mut(), |r| r as *mut f64);
332 let pmax = link_max.map_or(std::ptr::null_mut(), |r| r as *mut f64);
333 unsafe { sys::ImPlot_SetNextAxisLinks(axis, pmin, pmax) }
334 }
335
336 pub fn set_next_axes_limits(
338 &self,
339 x_min: f64,
340 x_max: f64,
341 y_min: f64,
342 y_max: f64,
343 cond: PlotCond,
344 ) {
345 unsafe {
346 sys::ImPlot_SetNextAxesLimits(x_min, x_max, y_min, y_max, cond as sys::ImPlotCond)
347 }
348 }
349
350 pub fn set_next_axes_to_fit(&self) {
352 unsafe { sys::ImPlot_SetNextAxesToFit() }
353 }
354
355 pub fn set_next_axis_to_fit(&self, axis: i32) {
357 unsafe { sys::ImPlot_SetNextAxisToFit(axis as sys::ImAxis) }
358 }
359
360 pub fn set_next_x_axis_to_fit(&self, axis: XAxis) {
362 unsafe { sys::ImPlot_SetNextAxisToFit(axis as sys::ImAxis) }
363 }
364
365 pub fn set_next_y_axis_to_fit(&self, axis: YAxis) {
367 unsafe { sys::ImPlot_SetNextAxisToFit(axis as sys::ImAxis) }
368 }
369
370 pub fn setup_x_axis_ticks_positions(
372 &self,
373 axis: XAxis,
374 values: &[f64],
375 labels: Option<&[&str]>,
376 keep_default: bool,
377 ) {
378 let cstrs: Option<Vec<std::ffi::CString>> = labels.map(|ls| {
379 ls.iter()
380 .map(|&s| std::ffi::CString::new(s).unwrap_or_default())
381 .collect()
382 });
383 if let Some(vec) = cstrs {
385 let raw: Vec<*const i8> = vec.iter().map(|c| c.as_ptr()).collect();
386 unsafe {
387 sys::ImPlot_SetupAxisTicks_doublePtr(
388 axis as sys::ImAxis,
389 values.as_ptr(),
390 values.len() as i32,
391 raw.as_ptr(),
392 keep_default,
393 )
394 }
395 } else {
396 unsafe {
397 sys::ImPlot_SetupAxisTicks_doublePtr(
398 axis as sys::ImAxis,
399 values.as_ptr(),
400 values.len() as i32,
401 std::ptr::null(),
402 keep_default,
403 )
404 }
405 }
406 }
407
408 pub fn setup_y_axis_ticks_positions(
410 &self,
411 axis: YAxis,
412 values: &[f64],
413 labels: Option<&[&str]>,
414 keep_default: bool,
415 ) {
416 let cstrs: Option<Vec<std::ffi::CString>> = labels.map(|ls| {
417 ls.iter()
418 .map(|&s| std::ffi::CString::new(s).unwrap_or_default())
419 .collect()
420 });
421 if let Some(cstrs) = cstrs {
422 let raw: Vec<*const i8> = cstrs.iter().map(|c| c.as_ptr()).collect();
423 unsafe {
424 sys::ImPlot_SetupAxisTicks_doublePtr(
425 axis as sys::ImAxis,
426 values.as_ptr(),
427 values.len() as i32,
428 raw.as_ptr(),
429 keep_default,
430 )
431 }
432 } else {
433 unsafe {
434 sys::ImPlot_SetupAxisTicks_doublePtr(
435 axis as sys::ImAxis,
436 values.as_ptr(),
437 values.len() as i32,
438 std::ptr::null(),
439 keep_default,
440 )
441 }
442 }
443 }
444
445 pub fn setup_x_axis_ticks_range(
447 &self,
448 axis: XAxis,
449 v_min: f64,
450 v_max: f64,
451 n_ticks: i32,
452 labels: Option<&[&str]>,
453 keep_default: bool,
454 ) {
455 let cstrs: Option<Vec<std::ffi::CString>> = labels.map(|ls| {
456 ls.iter()
457 .map(|&s| std::ffi::CString::new(s).unwrap_or_default())
458 .collect()
459 });
460 if let Some(cstrs) = cstrs {
461 let raw: Vec<*const i8> = cstrs.iter().map(|c| c.as_ptr()).collect();
462 unsafe {
463 sys::ImPlot_SetupAxisTicks_double(
464 axis as sys::ImAxis,
465 v_min,
466 v_max,
467 n_ticks,
468 raw.as_ptr(),
469 keep_default,
470 )
471 }
472 } else {
473 unsafe {
474 sys::ImPlot_SetupAxisTicks_double(
475 axis as sys::ImAxis,
476 v_min,
477 v_max,
478 n_ticks,
479 std::ptr::null(),
480 keep_default,
481 )
482 }
483 }
484 }
485
486 pub fn setup_y_axis_ticks_range(
488 &self,
489 axis: YAxis,
490 v_min: f64,
491 v_max: f64,
492 n_ticks: i32,
493 labels: Option<&[&str]>,
494 keep_default: bool,
495 ) {
496 let cstrs: Option<Vec<std::ffi::CString>> = labels.map(|ls| {
497 ls.iter()
498 .map(|&s| std::ffi::CString::new(s).unwrap_or_default())
499 .collect()
500 });
501 if let Some(cstrs) = cstrs {
502 let raw: Vec<*const i8> = cstrs.iter().map(|c| c.as_ptr()).collect();
503 unsafe {
504 sys::ImPlot_SetupAxisTicks_double(
505 axis as sys::ImAxis,
506 v_min,
507 v_max,
508 n_ticks,
509 raw.as_ptr(),
510 keep_default,
511 )
512 }
513 } else {
514 unsafe {
515 sys::ImPlot_SetupAxisTicks_double(
516 axis as sys::ImAxis,
517 v_min,
518 v_max,
519 n_ticks,
520 std::ptr::null(),
521 keep_default,
522 )
523 }
524 }
525 }
526
527 pub fn setup_x_axis_format(&self, axis: XAxis, fmt: &str) {
529 if let Ok(c) = std::ffi::CString::new(fmt) {
530 unsafe { sys::ImPlot_SetupAxisFormat_Str(axis as sys::ImAxis, c.as_ptr()) }
531 }
532 }
533
534 pub fn setup_y_axis_format(&self, axis: YAxis, fmt: &str) {
536 if let Ok(c) = std::ffi::CString::new(fmt) {
537 unsafe { sys::ImPlot_SetupAxisFormat_Str(axis as sys::ImAxis, c.as_ptr()) }
538 }
539 }
540
541 pub fn setup_x_axis_scale(&self, axis: XAxis, scale: sys::ImPlotScale) {
543 unsafe { sys::ImPlot_SetupAxisScale_PlotScale(axis as sys::ImAxis, scale) }
544 }
545
546 pub fn setup_y_axis_scale(&self, axis: YAxis, scale: sys::ImPlotScale) {
548 unsafe { sys::ImPlot_SetupAxisScale_PlotScale(axis as sys::ImAxis, scale) }
549 }
550
551 pub fn setup_axis_limits_constraints(&self, axis: i32, v_min: f64, v_max: f64) {
553 unsafe { sys::ImPlot_SetupAxisLimitsConstraints(axis as sys::ImAxis, v_min, v_max) }
554 }
555
556 pub fn setup_axis_zoom_constraints(&self, axis: i32, z_min: f64, z_max: f64) {
558 unsafe { sys::ImPlot_SetupAxisZoomConstraints(axis as sys::ImAxis, z_min, z_max) }
559 }
560
561 pub fn setup_x_axis_format_closure<F>(&self, axis: XAxis, f: F) -> AxisFormatterToken
564 where
565 F: Fn(f64) -> String + Send + Sync + 'static,
566 {
567 AxisFormatterToken::new(axis as sys::ImAxis, f)
568 }
569
570 pub fn setup_y_axis_format_closure<F>(&self, axis: YAxis, f: F) -> AxisFormatterToken
572 where
573 F: Fn(f64) -> String + Send + Sync + 'static,
574 {
575 AxisFormatterToken::new(axis as sys::ImAxis, f)
576 }
577
578 pub fn setup_x_axis_transform_closure<FW, INV>(
581 &self,
582 axis: XAxis,
583 forward: FW,
584 inverse: INV,
585 ) -> AxisTransformToken
586 where
587 FW: Fn(f64) -> f64 + Send + Sync + 'static,
588 INV: Fn(f64) -> f64 + Send + Sync + 'static,
589 {
590 AxisTransformToken::new(axis as sys::ImAxis, forward, inverse)
591 }
592
593 pub fn setup_y_axis_transform_closure<FW, INV>(
595 &self,
596 axis: YAxis,
597 forward: FW,
598 inverse: INV,
599 ) -> AxisTransformToken
600 where
601 FW: Fn(f64) -> f64 + Send + Sync + 'static,
602 INV: Fn(f64) -> f64 + Send + Sync + 'static,
603 {
604 AxisTransformToken::new(axis as sys::ImAxis, forward, inverse)
605 }
606}
607
608struct FormatterHolder {
611 func: Box<dyn Fn(f64) -> String + Send + Sync + 'static>,
612}
613
614pub struct AxisFormatterToken {
615 holder: Box<FormatterHolder>,
616 axis: sys::ImAxis,
617}
618
619impl AxisFormatterToken {
620 fn new<F>(axis: sys::ImAxis, f: F) -> Self
621 where
622 F: Fn(f64) -> String + Send + Sync + 'static,
623 {
624 let holder = Box::new(FormatterHolder { func: Box::new(f) });
625 let user = &*holder as *const FormatterHolder as *mut std::os::raw::c_void;
626 unsafe {
627 sys::ImPlot_SetupAxisFormat_PlotFormatter(
628 axis as sys::ImAxis,
629 Some(formatter_thunk),
630 user,
631 )
632 }
633 Self { holder, axis }
634 }
635}
636
637impl Drop for AxisFormatterToken {
638 fn drop(&mut self) {
639 }
641}
642
643unsafe extern "C" fn formatter_thunk(
644 value: f64,
645 buff: *mut std::os::raw::c_char,
646 size: std::os::raw::c_int,
647 user_data: *mut std::os::raw::c_void,
648) -> std::os::raw::c_int {
649 if user_data.is_null() || buff.is_null() || size <= 0 {
650 return 0;
651 }
652 let holder = unsafe { &*(user_data as *const FormatterHolder) };
653 let s = (holder.func)(value);
654 let bytes = s.as_bytes();
655 let max = (size - 1).max(0) as usize;
656 let n = bytes.len().min(max);
657 std::ptr::copy_nonoverlapping(bytes.as_ptr(), buff as *mut u8, n);
658 *buff.add(n) = 0;
659 n as std::os::raw::c_int
660}
661
662struct TransformHolder {
665 forward: Box<dyn Fn(f64) -> f64 + Send + Sync + 'static>,
666 inverse: Box<dyn Fn(f64) -> f64 + Send + Sync + 'static>,
667}
668
669pub struct AxisTransformToken {
670 holder: Box<TransformHolder>,
671 axis: sys::ImAxis,
672}
673
674impl AxisTransformToken {
675 fn new<FW, INV>(axis: sys::ImAxis, forward: FW, inverse: INV) -> Self
676 where
677 FW: Fn(f64) -> f64 + Send + Sync + 'static,
678 INV: Fn(f64) -> f64 + Send + Sync + 'static,
679 {
680 let holder = Box::new(TransformHolder {
681 forward: Box::new(forward),
682 inverse: Box::new(inverse),
683 });
684 let user = &*holder as *const TransformHolder as *mut std::os::raw::c_void;
685 unsafe {
686 sys::ImPlot_SetupAxisScale_PlotTransform(
687 axis as sys::ImAxis,
688 Some(transform_forward_thunk),
689 Some(transform_inverse_thunk),
690 user,
691 )
692 }
693 Self { holder, axis }
694 }
695}
696
697impl Drop for AxisTransformToken {
698 fn drop(&mut self) {
699 }
701}
702
703unsafe extern "C" fn transform_forward_thunk(
704 value: f64,
705 user_data: *mut std::os::raw::c_void,
706) -> f64 {
707 let holder = unsafe { &*(user_data as *const TransformHolder) };
708 (holder.forward)(value)
709}
710
711unsafe extern "C" fn transform_inverse_thunk(
712 value: f64,
713 user_data: *mut std::os::raw::c_void,
714) -> f64 {
715 let holder = unsafe { &*(user_data as *const TransformHolder) };
716 (holder.inverse)(value)
717}
718
719pub struct PlotToken<'ui> {
723 _lifetime: std::marker::PhantomData<&'ui ()>,
724}
725
726impl<'ui> PlotToken<'ui> {
727 pub(crate) fn new() -> Self {
729 Self {
730 _lifetime: std::marker::PhantomData,
731 }
732 }
733
734 pub fn end(self) {
739 }
741}
742
743impl<'ui> Drop for PlotToken<'ui> {
744 fn drop(&mut self) {
745 unsafe {
746 sys::ImPlot_EndPlot();
747 }
748 }
749}