sklears_inspection/visualization/
interactive_framework.rs1use super::config_types::PlotConfig;
14use crate::Float;
15use scirs2_core::ndarray::Array2;
16use std::collections::HashMap;
17
18type UpdateCallbackFn = Box<dyn Fn(&str, &Array2<Float>) + Send + Sync>;
20
21pub struct InteractiveVisualizer {
23 pub plot_configs: HashMap<String, PlotConfig>,
25 pub update_callbacks: Vec<UpdateCallbackFn>,
27 pub real_time_updates: bool,
29}
30
31impl std::fmt::Debug for InteractiveVisualizer {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 f.debug_struct("InteractiveVisualizer")
34 .field("plot_configs", &self.plot_configs)
35 .field("callback_count", &self.update_callbacks.len())
36 .field("real_time_updates", &self.real_time_updates)
37 .finish()
38 }
39}
40
41impl InteractiveVisualizer {
42 pub fn new() -> Self {
53 Self {
54 plot_configs: HashMap::new(),
55 update_callbacks: Vec::new(),
56 real_time_updates: false,
57 }
58 }
59
60 pub fn enable_real_time_updates(&mut self) {
62 self.real_time_updates = true;
63 }
64
65 pub fn disable_real_time_updates(&mut self) {
67 self.real_time_updates = false;
68 }
69
70 pub fn is_real_time_enabled(&self) -> bool {
72 self.real_time_updates
73 }
74
75 pub fn add_update_callback<F>(&mut self, callback: F)
77 where
78 F: Fn(&str, &Array2<Float>) + Send + Sync + 'static,
79 {
80 self.update_callbacks.push(Box::new(callback));
81 }
82
83 pub fn clear_callbacks(&mut self) {
85 self.update_callbacks.clear();
86 }
87
88 pub fn callback_count(&self) -> usize {
90 self.update_callbacks.len()
91 }
92
93 pub fn update_plot_data(&self, plot_id: &str, data: &Array2<Float>) {
95 if self.real_time_updates {
96 for callback in &self.update_callbacks {
97 callback(plot_id, data);
98 }
99 }
100 }
101
102 pub fn set_plot_config(&mut self, plot_id: String, config: PlotConfig) {
104 self.plot_configs.insert(plot_id, config);
105 }
106
107 pub fn get_plot_config(&self, plot_id: &str) -> Option<&PlotConfig> {
109 self.plot_configs.get(plot_id)
110 }
111
112 pub fn remove_plot(&mut self, plot_id: &str) -> Option<PlotConfig> {
114 self.plot_configs.remove(plot_id)
115 }
116
117 pub fn plot_ids(&self) -> Vec<&String> {
119 self.plot_configs.keys().collect()
120 }
121
122 pub fn plot_count(&self) -> usize {
124 self.plot_configs.len()
125 }
126
127 pub fn clear_plots(&mut self) {
129 self.plot_configs.clear();
130 }
131}
132
133impl Default for InteractiveVisualizer {
134 fn default() -> Self {
135 Self::new()
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::super::config_types::PlotConfig;
142 use super::*;
143 use scirs2_core::ndarray::array;
144
145 #[test]
146 fn test_interactive_visualizer_creation() {
147 let visualizer = InteractiveVisualizer::new();
148 assert!(!visualizer.real_time_updates);
149 assert_eq!(visualizer.callback_count(), 0);
150 assert_eq!(visualizer.plot_count(), 0);
151 }
152
153 #[test]
154 fn test_real_time_updates() {
155 let mut visualizer = InteractiveVisualizer::new();
156 assert!(!visualizer.is_real_time_enabled());
157
158 visualizer.enable_real_time_updates();
159 assert!(visualizer.is_real_time_enabled());
160
161 visualizer.disable_real_time_updates();
162 assert!(!visualizer.is_real_time_enabled());
163 }
164
165 #[test]
166 fn test_callbacks() {
167 let mut visualizer = InteractiveVisualizer::new();
168
169 visualizer.add_update_callback(|_id, _data| {
171 });
173 assert_eq!(visualizer.callback_count(), 1);
174
175 visualizer.clear_callbacks();
177 assert_eq!(visualizer.callback_count(), 0);
178 }
179
180 #[test]
181 fn test_plot_management() {
182 let mut visualizer = InteractiveVisualizer::new();
183 let config = PlotConfig::default();
184 let plot_id = "test_plot".to_string();
185
186 visualizer.set_plot_config(plot_id.clone(), config.clone());
188 assert_eq!(visualizer.plot_count(), 1);
189 assert!(visualizer.get_plot_config("test_plot").is_some());
190
191 let mut updated_config = config.clone();
193 updated_config.title = "Updated Title".to_string();
194 visualizer.set_plot_config(plot_id.clone(), updated_config);
195 assert_eq!(visualizer.plot_count(), 1);
196 assert_eq!(
197 visualizer
198 .get_plot_config("test_plot")
199 .expect("operation should succeed")
200 .title,
201 "Updated Title"
202 );
203
204 let removed = visualizer.remove_plot("test_plot");
206 assert!(removed.is_some());
207 assert_eq!(visualizer.plot_count(), 0);
208 }
209
210 #[test]
211 fn test_plot_data_update() {
212 let mut visualizer = InteractiveVisualizer::new();
213 let data = array![[1.0, 2.0], [3.0, 4.0]];
214
215 visualizer.update_plot_data("test", &data);
217
218 visualizer.enable_real_time_updates();
220
221 use std::sync::atomic::{AtomicBool, Ordering};
223 use std::sync::Arc;
224
225 let callback_triggered = Arc::new(AtomicBool::new(false));
226 let callback_triggered_clone = callback_triggered.clone();
227
228 visualizer.add_update_callback(move |_id, _data| {
229 callback_triggered_clone.store(true, Ordering::Relaxed);
230 });
231
232 visualizer.update_plot_data("test", &data);
234 assert!(callback_triggered.load(Ordering::Relaxed));
235 }
236
237 #[test]
238 fn test_default_implementation() {
239 let visualizer = InteractiveVisualizer::default();
240 assert!(!visualizer.real_time_updates);
241 assert_eq!(visualizer.plot_count(), 0);
242 }
243}