Skip to main content

dear_implot/plots/
dummy.rs

1//! Dummy plot implementation
2
3use super::{PlotData, PlotError, plot_spec_from, with_plot_str_or_empty};
4use crate::{DummyFlags, ItemFlags, sys};
5
6/// Builder for dummy plots
7///
8/// Dummy plots add a legend entry without plotting any actual data.
9/// This is useful for creating custom legend entries or placeholders.
10pub struct DummyPlot<'a> {
11    label: &'a str,
12    flags: DummyFlags,
13    item_flags: ItemFlags,
14}
15
16impl<'a> DummyPlot<'a> {
17    /// Create a new dummy plot with the given label
18    pub fn new(label: &'a str) -> Self {
19        Self {
20            label,
21            flags: DummyFlags::NONE,
22            item_flags: ItemFlags::NONE,
23        }
24    }
25
26    /// Set dummy flags for customization
27    pub fn with_flags(mut self, flags: DummyFlags) -> Self {
28        self.flags = flags;
29        self
30    }
31
32    /// Set common item flags for this plot item (applies to all plot types)
33    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
34        self.item_flags = flags;
35        self
36    }
37
38    /// Validate the plot data
39    pub fn validate(&self) -> Result<(), PlotError> {
40        if self.label.is_empty() {
41            return Err(PlotError::InvalidData("Label cannot be empty".to_string()));
42        }
43        Ok(())
44    }
45
46    /// Plot the dummy entry
47    pub fn plot(self) {
48        with_plot_str_or_empty(self.label, |label_ptr| unsafe {
49            let spec = plot_spec_from(
50                self.flags.bits() | self.item_flags.bits(),
51                0,
52                crate::IMPLOT_AUTO,
53            );
54            sys::ImPlot_PlotDummy(label_ptr, spec);
55        })
56    }
57}
58
59impl<'a> PlotData for DummyPlot<'a> {
60    fn label(&self) -> &str {
61        self.label
62    }
63
64    fn data_len(&self) -> usize {
65        0 // Dummy plots have no actual data
66    }
67}
68
69/// Multiple dummy plots for creating legend sections
70pub struct MultiDummyPlot<'a> {
71    labels: Vec<&'a str>,
72    flags: DummyFlags,
73    item_flags: ItemFlags,
74}
75
76impl<'a> MultiDummyPlot<'a> {
77    /// Create multiple dummy plots
78    pub fn new(labels: Vec<&'a str>) -> Self {
79        Self {
80            labels,
81            flags: DummyFlags::NONE,
82            item_flags: ItemFlags::NONE,
83        }
84    }
85
86    /// Set dummy flags for all entries
87    pub fn with_flags(mut self, flags: DummyFlags) -> Self {
88        self.flags = flags;
89        self
90    }
91
92    /// Set common item flags for all dummy entries
93    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
94        self.item_flags = flags;
95        self
96    }
97
98    /// Validate the plot data
99    pub fn validate(&self) -> Result<(), PlotError> {
100        if self.labels.is_empty() {
101            return Err(PlotError::EmptyData);
102        }
103
104        for (i, &label) in self.labels.iter().enumerate() {
105            if label.is_empty() {
106                return Err(PlotError::InvalidData(format!(
107                    "Label at index {} cannot be empty",
108                    i
109                )));
110            }
111        }
112
113        Ok(())
114    }
115
116    /// Plot all dummy entries
117    pub fn plot(self) {
118        for &label in &self.labels {
119            let dummy_plot = DummyPlot::new(label)
120                .with_flags(self.flags)
121                .with_item_flags(self.item_flags);
122            dummy_plot.plot();
123        }
124    }
125}
126
127impl<'a> PlotData for MultiDummyPlot<'a> {
128    fn label(&self) -> &str {
129        "MultiDummy"
130    }
131
132    fn data_len(&self) -> usize {
133        self.labels.len()
134    }
135}
136
137/// Legend separator using dummy plots
138pub struct LegendSeparator<'a> {
139    label: &'a str,
140}
141
142impl<'a> LegendSeparator<'a> {
143    /// Create a legend separator with optional label
144    pub fn new(label: &'a str) -> Self {
145        Self { label }
146    }
147
148    /// Create an empty separator
149    pub fn empty() -> Self {
150        Self { label: "---" }
151    }
152
153    /// Plot the separator
154    pub fn plot(self) {
155        let dummy_plot = DummyPlot::new(self.label);
156        dummy_plot.plot();
157    }
158}
159
160/// Legend header using dummy plots
161pub struct LegendHeader<'a> {
162    title: &'a str,
163}
164
165impl<'a> LegendHeader<'a> {
166    /// Create a legend header
167    pub fn new(title: &'a str) -> Self {
168        Self { title }
169    }
170
171    /// Plot the header
172    pub fn plot(self) {
173        let dummy_plot = DummyPlot::new(self.title);
174        dummy_plot.plot();
175    }
176}
177
178/// Custom legend entry builder
179pub struct CustomLegendEntry<'a> {
180    label: &'a str,
181    flags: DummyFlags,
182    item_flags: ItemFlags,
183}
184
185impl<'a> CustomLegendEntry<'a> {
186    /// Create a custom legend entry
187    pub fn new(label: &'a str) -> Self {
188        Self {
189            label,
190            flags: DummyFlags::NONE,
191            item_flags: ItemFlags::NONE,
192        }
193    }
194
195    /// Set flags for the entry
196    pub fn with_flags(mut self, flags: DummyFlags) -> Self {
197        self.flags = flags;
198        self
199    }
200
201    /// Set common item flags for the entry
202    pub fn with_item_flags(mut self, flags: ItemFlags) -> Self {
203        self.item_flags = flags;
204        self
205    }
206
207    /// Plot the custom entry
208    pub fn plot(self) {
209        let dummy_plot = DummyPlot::new(self.label)
210            .with_flags(self.flags)
211            .with_item_flags(self.item_flags);
212        dummy_plot.plot();
213    }
214}
215
216/// Legend group for organizing related entries
217pub struct LegendGroup<'a> {
218    title: &'a str,
219    entries: Vec<&'a str>,
220    add_separator: bool,
221}
222
223impl<'a> LegendGroup<'a> {
224    /// Create a new legend group
225    pub fn new(title: &'a str, entries: Vec<&'a str>) -> Self {
226        Self {
227            title,
228            entries,
229            add_separator: true,
230        }
231    }
232
233    /// Disable separator after the group
234    pub fn no_separator(mut self) -> Self {
235        self.add_separator = false;
236        self
237    }
238
239    /// Plot the legend group
240    pub fn plot(self) {
241        // Plot the header
242        LegendHeader::new(self.title).plot();
243
244        // Plot all entries
245        for &entry in &self.entries {
246            DummyPlot::new(entry).plot();
247        }
248
249        // Add separator if requested
250        if self.add_separator && !self.entries.is_empty() {
251            LegendSeparator::empty().plot();
252        }
253    }
254}
255
256/// Convenience functions for common dummy plot patterns
257impl<'a> DummyPlot<'a> {
258    /// Create a separator dummy plot
259    pub fn separator() -> DummyPlot<'static> {
260        DummyPlot::new("---")
261    }
262
263    /// Create a spacer dummy plot
264    pub fn spacer() -> DummyPlot<'static> {
265        DummyPlot::new(" ")
266    }
267
268    /// Create a header dummy plot
269    pub fn header(title: &'a str) -> DummyPlot<'a> {
270        DummyPlot::new(title)
271    }
272}
273
274/// Macro for creating multiple dummy plots easily
275#[macro_export]
276macro_rules! dummy_plots {
277    ($($label:expr),* $(,)?) => {
278        {
279            let labels = vec![$($label),*];
280            $crate::plots::dummy::MultiDummyPlot::new(labels)
281        }
282    };
283}
284
285/// Macro for creating a legend group
286#[macro_export]
287macro_rules! legend_group {
288    ($title:expr, $($entry:expr),* $(,)?) => {
289        {
290            let entries = vec![$($entry),*];
291            $crate::plots::dummy::LegendGroup::new($title, entries)
292        }
293    };
294}