dear-implot 0.12.0

High-level Rust bindings to ImPlot with dear-imgui-rs integration
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
// Utility functions for ImPlot

use crate::{XAxis, YAxis, compat_ffi, sys};
use dear_imgui_rs::with_scratch_txt;

/// Check if the plot area is hovered
pub fn is_plot_hovered() -> bool {
    unsafe { sys::ImPlot_IsPlotHovered() }
}

/// Check if any subplots area is hovered
pub fn is_subplots_hovered() -> bool {
    unsafe { sys::ImPlot_IsSubplotsHovered() }
}

/// Check if a legend entry is hovered
pub fn is_legend_entry_hovered(label: &str) -> bool {
    let label = if label.contains('\0') { "" } else { label };
    with_scratch_txt(label, |ptr| unsafe {
        sys::ImPlot_IsLegendEntryHovered(ptr)
    })
}

/// Get the mouse position in plot coordinates
pub fn get_plot_mouse_position(y_axis_choice: Option<crate::YAxisChoice>) -> sys::ImPlotPoint {
    let x_axis = 0; // ImAxis_X1
    let y_axis = match y_axis_choice {
        Some(crate::YAxisChoice::First) => 3,  // ImAxis_Y1
        Some(crate::YAxisChoice::Second) => 4, // ImAxis_Y2
        Some(crate::YAxisChoice::Third) => 5,  // ImAxis_Y3
        None => 3,                             // Default to Y1
    };
    unsafe { sys::ImPlot_GetPlotMousePos(x_axis as sys::ImAxis, y_axis as sys::ImAxis) }
}

/// Get the mouse position in plot coordinates for specific axes
pub fn get_plot_mouse_position_axes(x_axis: XAxis, y_axis: YAxis) -> sys::ImPlotPoint {
    unsafe { sys::ImPlot_GetPlotMousePos(x_axis as sys::ImAxis, y_axis as sys::ImAxis) }
}

/// Convert pixels to plot coordinates
pub fn pixels_to_plot(
    pixel_position: [f32; 2],
    y_axis_choice: Option<crate::YAxisChoice>,
) -> sys::ImPlotPoint {
    // Map absolute pixel coordinates to plot coordinates using current plot's axes
    let y_index = match y_axis_choice {
        Some(crate::YAxisChoice::First) => 0,
        Some(crate::YAxisChoice::Second) => 1,
        Some(crate::YAxisChoice::Third) => 2,
        None => 0,
    };
    unsafe {
        let plot = sys::ImPlot_GetCurrentPlot();
        if plot.is_null() {
            return sys::ImPlotPoint { x: 0.0, y: 0.0 };
        }
        let x_axis_ptr = sys::ImPlotPlot_XAxis_Nil(plot, 0);
        let y_axis_ptr = sys::ImPlotPlot_YAxis_Nil(plot, y_index);
        let x = sys::ImPlotAxis_PixelsToPlot(x_axis_ptr, pixel_position[0]);
        let y = sys::ImPlotAxis_PixelsToPlot(y_axis_ptr, pixel_position[1]);
        sys::ImPlotPoint { x, y }
    }
}

/// Convert pixels to plot coordinates for specific axes
pub fn pixels_to_plot_axes(
    pixel_position: [f32; 2],
    x_axis: XAxis,
    y_axis: YAxis,
) -> sys::ImPlotPoint {
    unsafe {
        let plot = sys::ImPlot_GetCurrentPlot();
        if plot.is_null() {
            return sys::ImPlotPoint { x: 0.0, y: 0.0 };
        }
        let x_axis_ptr = sys::ImPlotPlot_XAxis_Nil(plot, x_axis as i32);
        let y_axis_ptr = sys::ImPlotPlot_YAxis_Nil(plot, y_axis.to_index());
        let x = sys::ImPlotAxis_PixelsToPlot(x_axis_ptr, pixel_position[0]);
        let y = sys::ImPlotAxis_PixelsToPlot(y_axis_ptr, pixel_position[1]);
        sys::ImPlotPoint { x, y }
    }
}

/// Convert plot coordinates to pixels
pub fn plot_to_pixels(
    plot_position: sys::ImPlotPoint,
    y_axis_choice: Option<crate::YAxisChoice>,
) -> [f32; 2] {
    let y_index = match y_axis_choice {
        Some(crate::YAxisChoice::First) => 0,
        Some(crate::YAxisChoice::Second) => 1,
        Some(crate::YAxisChoice::Third) => 2,
        None => 0,
    };
    unsafe {
        let plot = sys::ImPlot_GetCurrentPlot();
        if plot.is_null() {
            return [0.0, 0.0];
        }
        let x_axis_ptr = sys::ImPlotPlot_XAxis_Nil(plot, 0);
        let y_axis_ptr = sys::ImPlotPlot_YAxis_Nil(plot, y_index);
        let px = sys::ImPlotAxis_PlotToPixels(x_axis_ptr, plot_position.x);
        let py = sys::ImPlotAxis_PlotToPixels(y_axis_ptr, plot_position.y);
        [px, py]
    }
}

/// Convert plot coordinates to pixels for specific axes
pub fn plot_to_pixels_axes(
    plot_position: sys::ImPlotPoint,
    x_axis: XAxis,
    y_axis: YAxis,
) -> [f32; 2] {
    unsafe {
        let plot = sys::ImPlot_GetCurrentPlot();
        if plot.is_null() {
            return [0.0, 0.0];
        }
        let x_axis_ptr = sys::ImPlotPlot_XAxis_Nil(plot, x_axis as i32);
        let y_axis_ptr = sys::ImPlotPlot_YAxis_Nil(plot, y_axis.to_index());
        let px = sys::ImPlotAxis_PlotToPixels(x_axis_ptr, plot_position.x);
        let py = sys::ImPlotAxis_PlotToPixels(y_axis_ptr, plot_position.y);
        [px, py]
    }
}

/// Get the current plot limits
pub fn get_plot_limits(
    _x_axis_choice: Option<crate::YAxisChoice>,
    y_axis_choice: Option<crate::YAxisChoice>,
) -> sys::ImPlotRect {
    let x_axis = 0; // ImAxis_X1
    let y_axis = match y_axis_choice {
        Some(crate::YAxisChoice::First) => 3,  // ImAxis_Y1
        Some(crate::YAxisChoice::Second) => 4, // ImAxis_Y2
        Some(crate::YAxisChoice::Third) => 5,  // ImAxis_Y3
        None => 3,                             // Default to Y1
    };
    unsafe { sys::ImPlot_GetPlotLimits(x_axis, y_axis) }
}

/// Whether a plot has an active selection region
pub fn is_plot_selected() -> bool {
    unsafe { sys::ImPlot_IsPlotSelected() }
}

/// Get the current plot selection rectangle for specific axes
pub fn get_plot_selection_axes(x_axis: XAxis, y_axis: YAxis) -> Option<sys::ImPlotRect> {
    if !is_plot_selected() {
        return None;
    }
    let rect = unsafe { sys::ImPlot_GetPlotSelection(x_axis as i32, y_axis as i32) };
    Some(rect)
}

/// Draw a simple round annotation marker at (x,y)
pub fn annotation_point(
    x: f64,
    y: f64,
    color: [f32; 4],
    pixel_offset: [f32; 2],
    clamp: bool,
    round: bool,
) {
    let col = sys::ImVec4_c {
        x: color[0],
        y: color[1],
        z: color[2],
        w: color[3],
    };
    let off = sys::ImVec2_c {
        x: pixel_offset[0],
        y: pixel_offset[1],
    };
    unsafe { sys::ImPlot_Annotation_Bool(x, y, col, off, clamp, round) }
}

/// Draw a text annotation at (x,y) using the non-variadic `ImPlot_Annotation_Str0` API.
///
/// This avoids calling the C variadic (`...`) entrypoint, which is not supported on some targets
/// (e.g. wasm32 via import-style bindings).
pub fn annotation_text(
    x: f64,
    y: f64,
    color: [f32; 4],
    pixel_offset: [f32; 2],
    clamp: bool,
    text: &str,
) {
    let col = sys::ImVec4_c {
        x: color[0],
        y: color[1],
        z: color[2],
        w: color[3],
    };
    let off = sys::ImVec2_c {
        x: pixel_offset[0],
        y: pixel_offset[1],
    };
    assert!(!text.contains('\0'), "text contained NUL");
    with_scratch_txt(text, |ptr| unsafe {
        compat_ffi::ImPlot_Annotation_Str0(x, y, col, off, clamp, ptr)
    })
}

/// Tag the X axis at position x with a tick-like mark
pub fn tag_x(x: f64, color: [f32; 4], round: bool) {
    let col = sys::ImVec4_c {
        x: color[0],
        y: color[1],
        z: color[2],
        w: color[3],
    };
    unsafe { sys::ImPlot_TagX_Bool(x, col, round) }
}

/// Tag the X axis at position x with a text label using the non-variadic `ImPlot_TagX_Str0` API.
pub fn tag_x_text(x: f64, color: [f32; 4], text: &str) {
    let col = sys::ImVec4_c {
        x: color[0],
        y: color[1],
        z: color[2],
        w: color[3],
    };
    assert!(!text.contains('\0'), "text contained NUL");
    with_scratch_txt(text, |ptr| unsafe {
        compat_ffi::ImPlot_TagX_Str0(x, col, ptr)
    })
}

/// Tag the Y axis at position y with a tick-like mark
pub fn tag_y(y: f64, color: [f32; 4], round: bool) {
    let col = sys::ImVec4_c {
        x: color[0],
        y: color[1],
        z: color[2],
        w: color[3],
    };
    unsafe { sys::ImPlot_TagY_Bool(y, col, round) }
}

/// Tag the Y axis at position y with a text label using the non-variadic `ImPlot_TagY_Str0` API.
pub fn tag_y_text(y: f64, color: [f32; 4], text: &str) {
    let col = sys::ImVec4_c {
        x: color[0],
        y: color[1],
        z: color[2],
        w: color[3],
    };
    assert!(!text.contains('\0'), "text contained NUL");
    with_scratch_txt(text, |ptr| unsafe {
        compat_ffi::ImPlot_TagY_Str0(y, col, ptr)
    })
}

/// Get the current plot limits for specific axes
pub fn get_plot_limits_axes(x_axis: XAxis, y_axis: YAxis) -> sys::ImPlotRect {
    unsafe { sys::ImPlot_GetPlotLimits(x_axis as i32, y_axis as i32) }
}

/// Check if an axis is hovered
pub fn is_axis_hovered(axis: i32) -> bool {
    unsafe { sys::ImPlot_IsAxisHovered(axis) }
}

/// Check if the X axis is hovered
pub fn is_plot_x_axis_hovered() -> bool {
    is_axis_hovered(XAxis::X1 as i32)
}

/// Check if a specific X axis is hovered
pub fn is_plot_x_axis_hovered_axis(x_axis: XAxis) -> bool {
    is_axis_hovered(x_axis as i32)
}

/// Check if a Y axis is hovered
pub fn is_plot_y_axis_hovered(y_axis_choice: Option<crate::YAxisChoice>) -> bool {
    let y_axis = match y_axis_choice {
        Some(crate::YAxisChoice::First) => 3,  // ImAxis_Y1
        Some(crate::YAxisChoice::Second) => 4, // ImAxis_Y2
        Some(crate::YAxisChoice::Third) => 5,  // ImAxis_Y3
        None => 3,                             // Default to Y1
    };
    is_axis_hovered(y_axis)
}

/// Check if a specific Y axis is hovered
pub fn is_plot_y_axis_hovered_axis(y_axis: YAxis) -> bool {
    is_axis_hovered(y_axis as i32)
}

/// Show the ImPlot demo window (requires sys demo symbols to be linked)
#[cfg(feature = "demo")]
pub fn show_demo_window(show: &mut bool) {
    unsafe { sys::ImPlot_ShowDemoWindow(show) }
}

/// Stub when demo feature is disabled
#[cfg(not(feature = "demo"))]
pub fn show_demo_window(_show: &mut bool) {}

/// Show the built-in user guide for ImPlot
pub fn show_user_guide() {
    unsafe { sys::ImPlot_ShowUserGuide() }
}

/// Show the metrics window (pass &mut bool for open state)
pub fn show_metrics_window(open: &mut bool) {
    unsafe { sys::ImPlot_ShowMetricsWindow(open as *mut bool) }
}

/// Get current plot position (top-left) in pixels
pub fn get_plot_pos() -> [f32; 2] {
    let out = unsafe { crate::compat_ffi::ImPlot_GetPlotPos() };
    [out.x, out.y]
}

/// Get current plot size in pixels
pub fn get_plot_size() -> [f32; 2] {
    let out = unsafe { crate::compat_ffi::ImPlot_GetPlotSize() };
    [out.x, out.y]
}

/// Get the underlying ImDrawList for the current plot (unsafe pointer)
pub fn get_plot_draw_list() -> *mut sys::ImDrawList {
    unsafe { sys::ImPlot_GetPlotDrawList() }
}

/// Push plot clip rect
pub fn push_plot_clip_rect(expand: f32) {
    unsafe { sys::ImPlot_PushPlotClipRect(expand) }
}

/// Pop plot clip rect
pub fn pop_plot_clip_rect() {
    unsafe { sys::ImPlot_PopPlotClipRect() }
}

/// Result of a drag interaction
#[derive(Debug, Clone, Copy, Default)]
pub struct DragResult {
    /// True if the underlying value changed this frame
    pub changed: bool,
    /// True if it was clicked this frame
    pub clicked: bool,
    /// True if hovered this frame
    pub hovered: bool,
    /// True if held/active this frame
    pub held: bool,
}

fn color4(rgba: [f32; 4]) -> sys::ImVec4_c {
    sys::ImVec4_c {
        x: rgba[0],
        y: rgba[1],
        z: rgba[2],
        w: rgba[3],
    }
}

/// Draggable point with result flags
pub fn drag_point(
    id: i32,
    x: &mut f64,
    y: &mut f64,
    color: [f32; 4],
    size: f32,
    flags: crate::DragToolFlags,
) -> DragResult {
    let mut clicked = false;
    let mut hovered = false;
    let mut held = false;
    let changed = unsafe {
        sys::ImPlot_DragPoint(
            id,
            x as *mut f64,
            y as *mut f64,
            color4(color),
            size,
            flags.bits() as i32,
            &mut clicked as *mut bool,
            &mut hovered as *mut bool,
            &mut held as *mut bool,
        )
    };
    DragResult {
        changed,
        clicked,
        hovered,
        held,
    }
}

/// Draggable vertical line at x
pub fn drag_line_x(
    id: i32,
    x: &mut f64,
    color: [f32; 4],
    thickness: f32,
    flags: crate::DragToolFlags,
) -> DragResult {
    let mut clicked = false;
    let mut hovered = false;
    let mut held = false;
    let changed = unsafe {
        sys::ImPlot_DragLineX(
            id,
            x as *mut f64,
            color4(color),
            thickness,
            flags.bits() as i32,
            &mut clicked as *mut bool,
            &mut hovered as *mut bool,
            &mut held as *mut bool,
        )
    };
    DragResult {
        changed,
        clicked,
        hovered,
        held,
    }
}

/// Draggable horizontal line at y
pub fn drag_line_y(
    id: i32,
    y: &mut f64,
    color: [f32; 4],
    thickness: f32,
    flags: crate::DragToolFlags,
) -> DragResult {
    let mut clicked = false;
    let mut hovered = false;
    let mut held = false;
    let changed = unsafe {
        sys::ImPlot_DragLineY(
            id,
            y as *mut f64,
            color4(color),
            thickness,
            flags.bits() as i32,
            &mut clicked as *mut bool,
            &mut hovered as *mut bool,
            &mut held as *mut bool,
        )
    };
    DragResult {
        changed,
        clicked,
        hovered,
        held,
    }
}