use scirs2_core::numeric::{Float, FromPrimitive};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ColorScheme {
Default,
HighContrast,
Viridis,
Plasma,
Grayscale,
}
#[derive(Debug, Clone)]
pub struct ColorThreshold<F: Float> {
pub threshold: F,
pub above_color: String,
pub below_color: String,
pub auto_threshold: bool,
pub target_clusters: Option<usize>,
}
impl<F: Float + FromPrimitive> Default for ColorThreshold<F> {
fn default() -> Self {
Self {
threshold: F::zero(),
above_color: "#1f77b4".to_string(), below_color: "#ff7f0e".to_string(), auto_threshold: true,
target_clusters: Some(4),
}
}
}
#[derive(Debug, Clone)]
pub struct DendrogramConfig<F: Float> {
pub color_scheme: ColorScheme,
pub color_threshold: ColorThreshold<F>,
pub show_labels: bool,
pub show_distances: bool,
pub orientation: DendrogramOrientation,
pub line_width: f32,
pub font_size: f32,
pub truncate_mode: Option<TruncateMode>,
pub styling: DendrogramStyling,
}
#[derive(Debug, Clone)]
pub struct DendrogramStyling {
pub background_color: String,
pub branch_style: BranchStyle,
pub node_markers: NodeMarkerStyle,
pub label_style: LabelStyle,
pub grid: Option<GridStyle>,
pub shadows: bool,
pub border: Option<BorderStyle>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BranchStyle {
Solid,
Dashed,
Dotted,
DashDot,
}
#[derive(Debug, Clone)]
pub struct NodeMarkerStyle {
pub show_internal_nodes: bool,
pub show_leaf_nodes: bool,
pub markershape: MarkerShape,
pub marker_size: f32,
pub marker_color: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum MarkerShape {
Circle,
Square,
Triangle,
Diamond,
Cross,
}
#[derive(Debug, Clone)]
pub struct LabelStyle {
pub font_family: String,
pub font_weight: FontWeight,
pub color: String,
pub rotation: f32,
pub background: Option<String>,
pub padding: f32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum FontWeight {
Normal,
Bold,
Light,
}
#[derive(Debug, Clone)]
pub struct GridStyle {
pub show_horizontal: bool,
pub show_vertical: bool,
pub color: String,
pub line_width: f32,
pub style: BranchStyle,
}
#[derive(Debug, Clone)]
pub struct BorderStyle {
pub color: String,
pub width: f32,
pub radius: f32,
}
impl Default for DendrogramStyling {
fn default() -> Self {
Self {
background_color: "#ffffff".to_string(),
branch_style: BranchStyle::Solid,
node_markers: NodeMarkerStyle::default(),
label_style: LabelStyle::default(),
grid: None,
shadows: false,
border: None,
}
}
}
impl Default for NodeMarkerStyle {
fn default() -> Self {
Self {
show_internal_nodes: false,
show_leaf_nodes: true,
markershape: MarkerShape::Circle,
marker_size: 4.0,
marker_color: "#333333".to_string(),
}
}
}
impl Default for LabelStyle {
fn default() -> Self {
Self {
font_family: "Arial, sans-serif".to_string(),
font_weight: FontWeight::Normal,
color: "#000000".to_string(),
rotation: 0.0,
background: None,
padding: 2.0,
}
}
}
impl Default for GridStyle {
fn default() -> Self {
Self {
show_horizontal: true,
show_vertical: false,
color: "#e0e0e0".to_string(),
line_width: 0.5,
style: BranchStyle::Solid,
}
}
}
impl Default for BorderStyle {
fn default() -> Self {
Self {
color: "#cccccc".to_string(),
width: 1.0,
radius: 0.0,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DendrogramOrientation {
Top,
Bottom,
Left,
Right,
}
#[derive(Debug, Clone)]
pub enum TruncateMode {
LastMerges(usize),
DistanceThreshold(f64),
TopLevels(usize),
}
impl<F: Float + FromPrimitive> Default for DendrogramConfig<F> {
fn default() -> Self {
Self {
color_scheme: ColorScheme::Default,
color_threshold: ColorThreshold::default(),
show_labels: true,
show_distances: false,
orientation: DendrogramOrientation::Top,
line_width: 1.0,
font_size: 10.0,
truncate_mode: None,
styling: DendrogramStyling::default(),
}
}
}
#[derive(Debug, Clone)]
pub struct DendrogramPlot<F: Float> {
pub branches: Vec<Branch<F>>,
pub leaves: Vec<Leaf>,
pub colors: Vec<String>,
pub legend: Vec<LegendEntry>,
pub bounds: (F, F, F, F),
pub config: DendrogramConfig<F>,
}
#[derive(Debug, Clone)]
pub struct Branch<F: Float> {
pub start: (F, F),
pub end: (F, F),
pub distance: F,
pub cluster_id: Option<usize>,
pub color: String,
pub line_width: Option<f32>,
}
#[derive(Debug, Clone)]
pub struct Leaf {
pub position: (f64, f64),
pub label: String,
pub color: String,
pub data_index: usize,
}
#[derive(Debug, Clone)]
pub struct LegendEntry {
pub color: String,
pub label: String,
pub threshold: Option<f64>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_color_scheme_variants() {
let schemes = [
ColorScheme::Default,
ColorScheme::HighContrast,
ColorScheme::Viridis,
ColorScheme::Plasma,
ColorScheme::Grayscale,
];
for scheme in &schemes {
assert!(format!("{:?}", scheme).len() > 0);
}
}
#[test]
fn test_color_threshold_default() {
let threshold: ColorThreshold<f64> = ColorThreshold::default();
assert_eq!(threshold.threshold, 0.0);
assert!(threshold.auto_threshold);
assert_eq!(threshold.target_clusters, Some(4));
}
#[test]
fn test_dendrogram_config_default() {
let config: DendrogramConfig<f64> = DendrogramConfig::default();
assert_eq!(config.color_scheme, ColorScheme::Default);
assert!(config.show_labels);
assert!(!config.show_distances);
assert_eq!(config.orientation, DendrogramOrientation::Top);
}
#[test]
fn test_styling_defaults() {
let styling = DendrogramStyling::default();
assert_eq!(styling.background_color, "#ffffff");
assert_eq!(styling.branch_style, BranchStyle::Solid);
assert!(!styling.shadows);
}
#[test]
fn test_branch_style_variants() {
let styles = [
BranchStyle::Solid,
BranchStyle::Dashed,
BranchStyle::Dotted,
BranchStyle::DashDot,
];
for style in &styles {
assert!(format!("{:?}", style).len() > 0);
}
}
#[test]
fn test_marker_shape_variants() {
let shapes = [
MarkerShape::Circle,
MarkerShape::Square,
MarkerShape::Triangle,
MarkerShape::Diamond,
MarkerShape::Cross,
];
for shape in &shapes {
assert!(format!("{:?}", shape).len() > 0);
}
}
#[test]
fn test_truncate_mode_variants() {
let modes = [
TruncateMode::LastMerges(10),
TruncateMode::DistanceThreshold(0.5),
TruncateMode::TopLevels(5),
];
for mode in &modes {
assert!(format!("{:?}", mode).len() > 0);
}
}
}