sklears_inspection/visualization/
interactive.rs1use crate::{Float, SklResult};
7use scirs2_core::ndarray::{Array2, ArrayView2};
9use serde::{Deserialize, Serialize};
10use std::collections::HashMap;
11
12use super::core::PlotConfig;
13
14type UpdateCallback = Option<Box<dyn Fn(&ArrayView2<Float>) -> SklResult<()> + Send + Sync>>;
16
17pub struct InteractiveVisualizer {
19 config: PlotConfig,
21 update_callback: UpdateCallback,
23}
24
25impl std::fmt::Debug for InteractiveVisualizer {
26 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27 f.debug_struct("InteractiveVisualizer")
28 .field("config", &self.config)
29 .field("update_callback", &self.update_callback.is_some())
30 .finish()
31 }
32}
33
34impl InteractiveVisualizer {
35 pub fn new(config: PlotConfig) -> Self {
37 Self {
38 config,
39 update_callback: None,
40 }
41 }
42
43 pub fn with_update_callback<F>(mut self, callback: F) -> Self
45 where
46 F: Fn(&ArrayView2<Float>) -> SklResult<()> + Send + Sync + 'static,
47 {
48 self.update_callback = Some(Box::new(callback));
49 self
50 }
51
52 pub fn update_data(&self, data: &ArrayView2<Float>) -> SklResult<()> {
54 if let Some(ref callback) = self.update_callback {
55 callback(data)?;
56 }
57 Ok(())
58 }
59
60 pub fn enable_real_time(&mut self, enabled: bool) {
62 self.config.interactive = enabled;
63 }
64
65 pub fn config(&self) -> &PlotConfig {
67 &self.config
68 }
69}
70
71pub struct RealTimePlotUpdater {
73 data_buffer: HashMap<String, Array2<Float>>,
75 update_frequency: Float,
77 max_buffer_size: usize,
79 is_active: bool,
81 #[allow(dead_code)] config: PlotConfig,
84}
85
86impl RealTimePlotUpdater {
87 pub fn new(config: PlotConfig) -> Self {
89 Self {
90 data_buffer: HashMap::new(),
91 update_frequency: 30.0, max_buffer_size: 1000,
93 is_active: false,
94 config,
95 }
96 }
97
98 pub fn with_frequency(mut self, frequency: Float) -> Self {
100 self.update_frequency = frequency;
101 self
102 }
103
104 pub fn with_buffer_size(mut self, size: usize) -> Self {
106 self.max_buffer_size = size;
107 self
108 }
109
110 pub fn add_data(&mut self, series_name: String, data: Array2<Float>) -> SklResult<()> {
112 if data.nrows() > self.max_buffer_size {
113 return Err(crate::SklearsError::InvalidInput(
114 "Data size exceeds maximum buffer size".to_string(),
115 ));
116 }
117
118 self.data_buffer.insert(series_name, data);
119 Ok(())
120 }
121
122 pub fn start(&mut self) {
124 self.is_active = true;
125 }
126
127 pub fn stop(&mut self) {
129 self.is_active = false;
130 }
131
132 pub fn is_active(&self) -> bool {
134 self.is_active
135 }
136
137 pub fn get_buffer_data(&self) -> &HashMap<String, Array2<Float>> {
139 &self.data_buffer
140 }
141
142 pub fn clear_buffer(&mut self) {
144 self.data_buffer.clear();
145 }
146
147 pub fn update_frequency(&self) -> Float {
149 self.update_frequency
150 }
151}
152
153#[derive(Debug, Clone, Serialize, Deserialize)]
155pub struct PlotInteractionHandler {
156 pub zoom_enabled: bool,
158 pub pan_enabled: bool,
160 pub selection_enabled: bool,
162 pub hover_enabled: bool,
164 pub click_enabled: bool,
166 pub brush_enabled: bool,
168}
169
170impl Default for PlotInteractionHandler {
171 fn default() -> Self {
172 Self {
173 zoom_enabled: true,
174 pan_enabled: true,
175 selection_enabled: true,
176 hover_enabled: true,
177 click_enabled: true,
178 brush_enabled: false,
179 }
180 }
181}
182
183impl PlotInteractionHandler {
184 pub fn new() -> Self {
186 Self::default()
187 }
188
189 pub fn with_zoom(mut self, enabled: bool) -> Self {
191 self.zoom_enabled = enabled;
192 self
193 }
194
195 pub fn with_pan(mut self, enabled: bool) -> Self {
197 self.pan_enabled = enabled;
198 self
199 }
200
201 pub fn with_selection(mut self, enabled: bool) -> Self {
203 self.selection_enabled = enabled;
204 self
205 }
206
207 pub fn with_hover(mut self, enabled: bool) -> Self {
209 self.hover_enabled = enabled;
210 self
211 }
212
213 pub fn with_click(mut self, enabled: bool) -> Self {
215 self.click_enabled = enabled;
216 self
217 }
218
219 pub fn with_brush(mut self, enabled: bool) -> Self {
221 self.brush_enabled = enabled;
222 self
223 }
224}
225
226#[cfg(test)]
227mod tests {
228 use super::*;
229 use scirs2_core::ndarray::array;
231
232 #[test]
233 fn test_interactive_visualizer_creation() {
234 let config = PlotConfig::default();
235 let visualizer = InteractiveVisualizer::new(config);
236
237 assert!(visualizer.update_callback.is_none());
238 assert!(visualizer.config().interactive);
239 }
240
241 #[test]
242 fn test_real_time_plot_updater() {
243 let config = PlotConfig::default();
244 let mut updater = RealTimePlotUpdater::new(config);
245
246 assert!(!updater.is_active());
247 assert_eq!(updater.update_frequency(), 30.0);
248
249 updater.start();
250 assert!(updater.is_active());
251
252 updater.stop();
253 assert!(!updater.is_active());
254 }
255
256 #[test]
257 fn test_plot_interaction_handler() {
258 let handler = PlotInteractionHandler::new()
259 .with_zoom(false)
260 .with_pan(true)
261 .with_selection(false);
262
263 assert!(!handler.zoom_enabled);
264 assert!(handler.pan_enabled);
265 assert!(!handler.selection_enabled);
266 assert!(handler.hover_enabled); }
268
269 #[test]
270 fn test_real_time_updater_data_buffer() {
271 let config = PlotConfig::default();
272 let mut updater = RealTimePlotUpdater::new(config);
273
274 let data = array![[1.0, 2.0], [3.0, 4.0]];
275 updater
276 .add_data("test_series".to_string(), data)
277 .expect("operation should succeed");
278
279 assert_eq!(updater.get_buffer_data().len(), 1);
280 assert!(updater.get_buffer_data().contains_key("test_series"));
281
282 updater.clear_buffer();
283 assert_eq!(updater.get_buffer_data().len(), 0);
284 }
285}