1use scirs2_core::numeric::{Float, FromPrimitive};
7use serde::{Deserialize, Serialize};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum ColorScheme {
12 Default,
14 HighContrast,
16 Viridis,
18 Plasma,
20 Grayscale,
22}
23
24#[derive(Debug, Clone)]
26pub struct ColorThreshold<F: Float> {
27 pub threshold: F,
29 pub above_color: String,
31 pub below_color: String,
33 pub auto_threshold: bool,
35 pub target_clusters: Option<usize>,
37}
38
39impl<F: Float + FromPrimitive> Default for ColorThreshold<F> {
40 fn default() -> Self {
41 Self {
42 threshold: F::zero(),
43 above_color: "#1f77b4".to_string(), below_color: "#ff7f0e".to_string(), auto_threshold: true,
46 target_clusters: Some(4),
47 }
48 }
49}
50
51#[derive(Debug, Clone)]
53pub struct DendrogramConfig<F: Float> {
54 pub color_scheme: ColorScheme,
56 pub color_threshold: ColorThreshold<F>,
58 pub show_labels: bool,
60 pub show_distances: bool,
62 pub orientation: DendrogramOrientation,
64 pub line_width: f32,
66 pub font_size: f32,
68 pub truncate_mode: Option<TruncateMode>,
70 pub styling: DendrogramStyling,
72}
73
74#[derive(Debug, Clone)]
76pub struct DendrogramStyling {
77 pub background_color: String,
79 pub branch_style: BranchStyle,
81 pub node_markers: NodeMarkerStyle,
83 pub label_style: LabelStyle,
85 pub grid: Option<GridStyle>,
87 pub shadows: bool,
89 pub border: Option<BorderStyle>,
91}
92
93#[derive(Debug, Clone, Copy, PartialEq, Eq)]
95pub enum BranchStyle {
96 Solid,
97 Dashed,
98 Dotted,
99 DashDot,
100}
101
102#[derive(Debug, Clone)]
104pub struct NodeMarkerStyle {
105 pub show_internal_nodes: bool,
107 pub show_leaf_nodes: bool,
109 pub markershape: MarkerShape,
111 pub marker_size: f32,
113 pub marker_color: String,
115}
116
117#[derive(Debug, Clone, Copy, PartialEq, Eq)]
119pub enum MarkerShape {
120 Circle,
121 Square,
122 Triangle,
123 Diamond,
124 Cross,
125}
126
127#[derive(Debug, Clone)]
129pub struct LabelStyle {
130 pub font_family: String,
132 pub font_weight: FontWeight,
134 pub color: String,
136 pub rotation: f32,
138 pub background: Option<String>,
140 pub padding: f32,
142}
143
144#[derive(Debug, Clone, Copy, PartialEq, Eq)]
146pub enum FontWeight {
147 Normal,
148 Bold,
149 Light,
150}
151
152#[derive(Debug, Clone)]
154pub struct GridStyle {
155 pub show_horizontal: bool,
157 pub show_vertical: bool,
159 pub color: String,
161 pub line_width: f32,
163 pub style: BranchStyle,
165}
166
167#[derive(Debug, Clone)]
169pub struct BorderStyle {
170 pub color: String,
172 pub width: f32,
174 pub radius: f32,
176}
177
178impl Default for DendrogramStyling {
179 fn default() -> Self {
180 Self {
181 background_color: "#ffffff".to_string(),
182 branch_style: BranchStyle::Solid,
183 node_markers: NodeMarkerStyle::default(),
184 label_style: LabelStyle::default(),
185 grid: None,
186 shadows: false,
187 border: None,
188 }
189 }
190}
191
192impl Default for NodeMarkerStyle {
193 fn default() -> Self {
194 Self {
195 show_internal_nodes: false,
196 show_leaf_nodes: true,
197 markershape: MarkerShape::Circle,
198 marker_size: 4.0,
199 marker_color: "#333333".to_string(),
200 }
201 }
202}
203
204impl Default for LabelStyle {
205 fn default() -> Self {
206 Self {
207 font_family: "Arial, sans-serif".to_string(),
208 font_weight: FontWeight::Normal,
209 color: "#000000".to_string(),
210 rotation: 0.0,
211 background: None,
212 padding: 2.0,
213 }
214 }
215}
216
217impl Default for GridStyle {
218 fn default() -> Self {
219 Self {
220 show_horizontal: true,
221 show_vertical: false,
222 color: "#e0e0e0".to_string(),
223 line_width: 0.5,
224 style: BranchStyle::Solid,
225 }
226 }
227}
228
229impl Default for BorderStyle {
230 fn default() -> Self {
231 Self {
232 color: "#cccccc".to_string(),
233 width: 1.0,
234 radius: 0.0,
235 }
236 }
237}
238
239#[derive(Debug, Clone, Copy, PartialEq, Eq)]
241pub enum DendrogramOrientation {
242 Top,
244 Bottom,
246 Left,
248 Right,
250}
251
252#[derive(Debug, Clone)]
254pub enum TruncateMode {
255 LastMerges(usize),
257 DistanceThreshold(f64),
259 TopLevels(usize),
261}
262
263impl<F: Float + FromPrimitive> Default for DendrogramConfig<F> {
264 fn default() -> Self {
265 Self {
266 color_scheme: ColorScheme::Default,
267 color_threshold: ColorThreshold::default(),
268 show_labels: true,
269 show_distances: false,
270 orientation: DendrogramOrientation::Top,
271 line_width: 1.0,
272 font_size: 10.0,
273 truncate_mode: None,
274 styling: DendrogramStyling::default(),
275 }
276 }
277}
278
279#[derive(Debug, Clone)]
281pub struct DendrogramPlot<F: Float> {
282 pub branches: Vec<Branch<F>>,
284 pub leaves: Vec<Leaf>,
286 pub colors: Vec<String>,
288 pub legend: Vec<LegendEntry>,
290 pub bounds: (F, F, F, F),
292 pub config: DendrogramConfig<F>,
294}
295
296#[derive(Debug, Clone)]
298pub struct Branch<F: Float> {
299 pub start: (F, F),
301 pub end: (F, F),
303 pub distance: F,
305 pub cluster_id: Option<usize>,
307 pub color: String,
309 pub line_width: Option<f32>,
311}
312
313#[derive(Debug, Clone)]
315pub struct Leaf {
316 pub position: (f64, f64),
318 pub label: String,
320 pub color: String,
322 pub data_index: usize,
324}
325
326#[derive(Debug, Clone)]
328pub struct LegendEntry {
329 pub color: String,
331 pub label: String,
333 pub threshold: Option<f64>,
335}
336
337#[cfg(test)]
338mod tests {
339 use super::*;
340
341 #[test]
342 fn test_color_scheme_variants() {
343 let schemes = [
344 ColorScheme::Default,
345 ColorScheme::HighContrast,
346 ColorScheme::Viridis,
347 ColorScheme::Plasma,
348 ColorScheme::Grayscale,
349 ];
350
351 for scheme in &schemes {
352 assert!(format!("{:?}", scheme).len() > 0);
353 }
354 }
355
356 #[test]
357 fn test_color_threshold_default() {
358 let threshold: ColorThreshold<f64> = ColorThreshold::default();
359 assert_eq!(threshold.threshold, 0.0);
360 assert!(threshold.auto_threshold);
361 assert_eq!(threshold.target_clusters, Some(4));
362 }
363
364 #[test]
365 fn test_dendrogram_config_default() {
366 let config: DendrogramConfig<f64> = DendrogramConfig::default();
367 assert_eq!(config.color_scheme, ColorScheme::Default);
368 assert!(config.show_labels);
369 assert!(!config.show_distances);
370 assert_eq!(config.orientation, DendrogramOrientation::Top);
371 }
372
373 #[test]
374 fn test_styling_defaults() {
375 let styling = DendrogramStyling::default();
376 assert_eq!(styling.background_color, "#ffffff");
377 assert_eq!(styling.branch_style, BranchStyle::Solid);
378 assert!(!styling.shadows);
379 }
380
381 #[test]
382 fn test_branch_style_variants() {
383 let styles = [
384 BranchStyle::Solid,
385 BranchStyle::Dashed,
386 BranchStyle::Dotted,
387 BranchStyle::DashDot,
388 ];
389
390 for style in &styles {
391 assert!(format!("{:?}", style).len() > 0);
392 }
393 }
394
395 #[test]
396 fn test_marker_shape_variants() {
397 let shapes = [
398 MarkerShape::Circle,
399 MarkerShape::Square,
400 MarkerShape::Triangle,
401 MarkerShape::Diamond,
402 MarkerShape::Cross,
403 ];
404
405 for shape in &shapes {
406 assert!(format!("{:?}", shape).len() > 0);
407 }
408 }
409
410 #[test]
411 fn test_truncate_mode_variants() {
412 let modes = [
413 TruncateMode::LastMerges(10),
414 TruncateMode::DistanceThreshold(0.5),
415 TruncateMode::TopLevels(5),
416 ];
417
418 for mode in &modes {
419 assert!(format!("{:?}", mode).len() > 0);
420 }
421 }
422}