1use eframe::{egui, NativeOptions};
22use liveplot::config::ScopeButton;
23use liveplot::{channel_plot, FeatureFlags, LivePlotApp, PlotPoint};
24use std::time::{Duration, SystemTime, UNIX_EPOCH};
25
26struct FeaturesApp {
28 plot: LivePlotApp,
29 features: FeatureFlags,
30 _sink: liveplot::PlotSink,
32 _tr_sine: liveplot::Trace,
33 _tr_cos: liveplot::Trace,
34}
35
36impl FeaturesApp {
37 fn new() -> Self {
38 let (sink, rx) = channel_plot();
40 let tr_sine = sink.create_trace("sine", None);
41 let tr_cos = sink.create_trace("cosine", None);
42
43 let sink_clone = sink.clone();
45 let sine_clone = tr_sine.clone();
46 let cos_clone = tr_cos.clone();
47 std::thread::spawn(move || {
48 const FS_HZ: f64 = 1000.0;
49 const F_HZ: f64 = 3.0;
50 let dt = Duration::from_millis(1);
51 let mut n: u64 = 0;
52 loop {
53 let t = n as f64 / FS_HZ;
54 let s_val = (2.0 * std::f64::consts::PI * F_HZ * t).sin();
55 let c_val = (2.0 * std::f64::consts::PI * F_HZ * t).cos();
56 let t_s = SystemTime::now()
57 .duration_since(UNIX_EPOCH)
58 .map(|d| d.as_secs_f64())
59 .unwrap_or(0.0);
60 let _ = sink_clone.send_point(&sine_clone, PlotPoint { x: t_s, y: s_val });
61 let _ = sink_clone.send_point(&cos_clone, PlotPoint { x: t_s, y: c_val });
62 n = n.wrapping_add(1);
63 std::thread::sleep(dt);
64 }
65 });
66
67 let plot = LivePlotApp::new(rx);
68 let features = FeatureFlags::default();
69
70 Self {
71 plot,
72 features,
73 _sink: sink,
74 _tr_sine: tr_sine,
75 _tr_cos: tr_cos,
76 }
77 }
78
79 fn apply_features(&mut self) {
84 let f = &self.features;
85 let panel = &mut self.plot.main_panel;
86
87 let mut btns = ScopeButton::all_defaults();
90 if !f.pause_resume {
91 btns.retain(|b| *b != ScopeButton::PauseResume);
92 }
93 if !f.clear_all {
94 btns.retain(|b| *b != ScopeButton::ClearAll);
95 }
96 if !f.scopes {
97 btns.retain(|b| *b != ScopeButton::Scopes);
100 }
101
102 panel.top_bar_buttons = if f.top_bar {
103 Some(btns.clone())
105 } else {
106 Some(vec![])
107 };
108 panel.sidebar_buttons = if f.sidebar { Some(btns) } else { Some(vec![]) };
109
110 for scope in panel.liveplot_panel.get_data_mut() {
112 scope.show_legend = f.legend;
113 scope.show_grid = f.grid;
114 }
115
116 panel.liveplot_panel.set_tick_label_thresholds(
118 if f.y_tick_labels {
119 250.0
120 } else {
121 f32::INFINITY
122 },
123 if f.x_tick_labels {
124 200.0
125 } else {
126 f32::INFINITY
127 },
128 );
129
130 {
135 let hk = panel.hotkeys.clone();
136
137 panel.right_side_panels.retain(|p| {
139 if p.downcast_ref::<liveplot::panels::traces_ui::TracesPanel>()
140 .is_some()
141 {
142 f.sidebar
143 } else if p
144 .downcast_ref::<liveplot::panels::math_ui::MathPanel>()
145 .is_some()
146 {
147 f.sidebar && f.math
148 } else if p
149 .downcast_ref::<liveplot::panels::hotkeys_ui::HotkeysPanel>()
150 .is_some()
151 {
152 f.sidebar && f.hotkeys
153 } else if p
154 .downcast_ref::<liveplot::panels::thresholds_ui::ThresholdsPanel>()
155 .is_some()
156 {
157 f.sidebar && f.thresholds
158 } else if p
159 .downcast_ref::<liveplot::panels::triggers_ui::TriggersPanel>()
160 .is_some()
161 {
162 f.sidebar && f.triggers
163 } else if p
164 .downcast_ref::<liveplot::panels::measurment_ui::MeasurementPanel>()
165 .is_some()
166 {
167 f.sidebar && f.measurement
168 } else {
169 true
171 }
172 });
173
174 if f.sidebar
176 && !panel.right_side_panels.iter().any(|p| {
177 p.downcast_ref::<liveplot::panels::traces_ui::TracesPanel>()
178 .is_some()
179 })
180 {
181 panel
182 .right_side_panels
183 .push(Box::new(liveplot::panels::traces_ui::TracesPanel::default()));
184 }
185 if f.sidebar
186 && f.math
187 && !panel.right_side_panels.iter().any(|p| {
188 p.downcast_ref::<liveplot::panels::math_ui::MathPanel>()
189 .is_some()
190 })
191 {
192 panel
193 .right_side_panels
194 .push(Box::new(liveplot::panels::math_ui::MathPanel::default()));
195 }
196 if f.sidebar
197 && f.hotkeys
198 && !panel.right_side_panels.iter().any(|p| {
199 p.downcast_ref::<liveplot::panels::hotkeys_ui::HotkeysPanel>()
200 .is_some()
201 })
202 {
203 panel.right_side_panels.push(Box::new(
204 liveplot::panels::hotkeys_ui::HotkeysPanel::new(hk.clone()),
205 ));
206 }
207 if f.sidebar
208 && f.thresholds
209 && !panel.right_side_panels.iter().any(|p| {
210 p.downcast_ref::<liveplot::panels::thresholds_ui::ThresholdsPanel>()
211 .is_some()
212 })
213 {
214 panel.right_side_panels.push(Box::new(
215 liveplot::panels::thresholds_ui::ThresholdsPanel::default(),
216 ));
217 }
218 if f.sidebar
219 && f.triggers
220 && !panel.right_side_panels.iter().any(|p| {
221 p.downcast_ref::<liveplot::panels::triggers_ui::TriggersPanel>()
222 .is_some()
223 })
224 {
225 panel.right_side_panels.push(Box::new(
226 liveplot::panels::triggers_ui::TriggersPanel::default(),
227 ));
228 }
229 if f.sidebar
230 && f.measurement
231 && !panel.right_side_panels.iter().any(|p| {
232 p.downcast_ref::<liveplot::panels::measurment_ui::MeasurementPanel>()
233 .is_some()
234 })
235 {
236 panel.right_side_panels.push(Box::new(
237 liveplot::panels::measurment_ui::MeasurementPanel::default(),
238 ));
239 }
240 }
241
242 #[cfg(feature = "fft")]
243 {
244 if f.fft {
251 let has_fft = panel.bottom_panels.iter().any(|p| {
252 p.downcast_ref::<liveplot::panels::fft_ui::FftPanel>()
253 .is_some()
254 });
255 if !has_fft {
256 panel
257 .bottom_panels
258 .push(Box::new(liveplot::panels::fft_ui::FftPanel::default()));
259 }
260 } else {
261 panel.bottom_panels.retain(|p| {
262 p.downcast_ref::<liveplot::panels::fft_ui::FftPanel>()
263 .is_none()
264 });
265 }
266 }
267
268 }
276}
277
278impl eframe::App for FeaturesApp {
279 fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
280 egui::TopBottomPanel::top("features_top").show(ctx, |ui| {
284 ui.label("Toggle features:");
285 ui.horizontal_wrapped(|ui| {
286 ui.checkbox(&mut self.features.top_bar, "top_bar");
287 ui.checkbox(&mut self.features.sidebar, "sidebar");
288 ui.checkbox(&mut self.features.markers, "markers");
289 ui.checkbox(&mut self.features.thresholds, "thresholds");
290 ui.checkbox(&mut self.features.triggers, "triggers");
291 ui.checkbox(&mut self.features.measurement, "measurement");
292 ui.checkbox(&mut self.features.export, "export");
293 ui.checkbox(&mut self.features.math, "math");
294 ui.checkbox(&mut self.features.hotkeys, "hotkeys");
295 ui.checkbox(&mut self.features.fft, "fft");
296 ui.checkbox(&mut self.features.x_tick_labels, "x_tick_labels");
297 ui.checkbox(&mut self.features.y_tick_labels, "y_tick_labels");
298 ui.checkbox(&mut self.features.grid, "grid");
299 ui.checkbox(&mut self.features.legend, "legend");
300 ui.checkbox(&mut self.features.scopes, "scopes");
301 ui.checkbox(&mut self.features.pause_resume, "pause_resume");
302 ui.checkbox(&mut self.features.clear_all, "clear_all");
303 });
304 });
305
306 self.apply_features();
308
309 egui::CentralPanel::default().show(ctx, |ui| {
311 self.plot.main_panel.update_embedded(ui);
312 });
313
314 ctx.request_repaint_after(Duration::from_millis(16));
316 }
317}
318
319fn main() -> eframe::Result<()> {
320 let app = FeaturesApp::new();
321 eframe::run_native(
322 "Feature Flags Example",
323 NativeOptions::default(),
324 Box::new(|_cc| Ok(Box::new(app))),
325 )
326}