#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Theme {
pub name: &'static str,
pub background_paint: &'static str,
pub background_ref: &'static str,
pub node_fill: &'static str,
pub node_stroke: &'static str,
pub node_text: &'static str,
pub line: &'static str,
pub arrowhead: &'static str,
pub edge_label_text: &'static str,
pub cluster_fill: &'static str,
pub cluster_stroke: &'static str,
pub cluster_text: &'static str,
pub state_marker: &'static str,
pub note_fill: &'static str,
pub note_stroke: &'static str,
pub note_text: &'static str,
pub lifeline: &'static str,
pub activation_fill: &'static str,
pub activation_stroke: &'static str,
pub axis: &'static str,
pub grid: &'static str,
pub series: &'static [&'static str],
pub series_text: &'static str,
}
pub const DARK: Theme = Theme {
name: "dark",
background_paint: "none",
background_ref: "#333333",
node_fill: "#1f2020",
node_stroke: "#cccccc",
node_text: "#cccccc",
line: "#d3d3d3",
arrowhead: "#d3d3d3",
edge_label_text: "#cccccc",
cluster_fill: "#2b2b38",
cluster_stroke: "#8a8a8a",
cluster_text: "#cccccc",
state_marker: "#d3d3d3",
note_fill: "#3d3a2a",
note_stroke: "#b8a94e",
note_text: "#f0e6bf",
lifeline: "#8a8a8a",
activation_fill: "#5a5a66",
activation_stroke: "#cccccc",
axis: "#8a8a8a",
grid: "#4a4a4a",
series: &[
"#4e79a7", "#4a8a42", "#b07aa1", "#9c755f", "#3f6b78", "#8c6d31", "#a8564c", "#6b6ecf",
],
series_text: "#f2f2f2",
};
pub const LIGHT: Theme = Theme {
name: "light",
background_paint: "none",
background_ref: "#ffffff",
node_fill: "#f8fafc",
node_stroke: "#94a3b8",
node_text: "#0f172a",
line: "#64748b",
arrowhead: "#64748b",
edge_label_text: "#0f172a",
cluster_fill: "#f1f5f9",
cluster_stroke: "#cbd5e1",
cluster_text: "#0f172a",
state_marker: "#64748b",
note_fill: "#fff8d5",
note_stroke: "#b8a94e",
note_text: "#3a3524",
lifeline: "#94a3b8",
activation_fill: "#dbe3ec",
activation_stroke: "#64748b",
axis: "#94a3b8",
grid: "#c8d4e0",
series: &[
"#7ea6d8", "#8fc98a", "#d3a7cb", "#c9ab97", "#86b3bd", "#cfae6a", "#e39b93", "#a4a7e0",
],
series_text: "#1a2230",
};
pub const CLASSIC: Theme = Theme {
name: "classic",
background_paint: "none",
background_ref: "#ffffff",
node_fill: "#ececff",
node_stroke: "#7b88a8",
node_text: "#333333",
line: "#5c6b8a",
arrowhead: "#5c6b8a",
edge_label_text: "#333333",
cluster_fill: "#ffffde",
cluster_stroke: "#aaaa33",
cluster_text: "#333333",
state_marker: "#5c6b8a",
note_fill: "#fff5ad",
note_stroke: "#aaaa33",
note_text: "#333333",
lifeline: "#9aa4bd",
activation_fill: "#dcdcf5",
activation_stroke: "#7b88a8",
axis: "#9aa4bd",
grid: "#cfd4de",
series: &[
"#9fb3d9", "#a7d3a0", "#dcb6d6", "#d5bda9", "#8bc0cc", "#e0c079", "#eeaaa2", "#b3b6ea",
],
series_text: "#242a36",
};
pub const FOREST: Theme = Theme {
name: "forest",
background_paint: "none",
background_ref: "#ffffff",
node_fill: "#cde498",
node_stroke: "#13540c",
node_text: "#333333",
line: "#008000",
arrowhead: "#008000",
edge_label_text: "#333333",
cluster_fill: "#cdffb2",
cluster_stroke: "#6eaa49",
cluster_text: "#333333",
state_marker: "#008000",
note_fill: "#fff5ad",
note_stroke: "#aaaa33",
note_text: "#333333",
lifeline: "#6eaa49",
activation_fill: "#e2f3cf",
activation_stroke: "#13540c",
axis: "#6eaa49",
grid: "#c3dfae",
series: &[
"#8fbf6a", "#b6dd9a", "#cfe3a8", "#7aa9a0", "#c8c07a", "#a2c3d6", "#d6b48f", "#8fa8d8",
],
series_text: "#1f2a17",
};
pub const NEUTRAL: Theme = Theme {
name: "neutral",
background_paint: "none",
background_ref: "#ffffff",
node_fill: "#eeeeee",
node_stroke: "#999999",
node_text: "#333333",
line: "#666666",
arrowhead: "#666666",
edge_label_text: "#333333",
cluster_fill: "#eaeaea",
cluster_stroke: "#999999",
cluster_text: "#333333",
state_marker: "#666666",
note_fill: "#f0f0e0",
note_stroke: "#999999",
note_text: "#333333",
lifeline: "#999999",
activation_fill: "#e0e0e0",
activation_stroke: "#666666",
axis: "#999999",
grid: "#d6d6d6",
series: &[
"#e7e7e7", "#818181", "#c5c5c5", "#929292", "#d6d6d6", "#a3a3a3", "#b4b4b4", "#707070",
],
series_text: "#1f1f1f",
};
pub const ALL: &[Theme] = &[DARK, LIGHT, CLASSIC, FOREST, NEUTRAL];
impl Theme {
pub fn series(&self, i: usize) -> &'static str {
self.series[i % self.series.len()]
}
pub fn named(name: &str) -> Theme {
match name {
"light" | "modern" => LIGHT,
"classic" | "mermaid" | "default" => CLASSIC,
"forest" => FOREST,
"neutral" => NEUTRAL,
_ => DARK,
}
}
pub fn luminance(hex: &str) -> f64 {
fn channel(c: u8) -> f64 {
let s = c as f64 / 255.0;
if s <= 0.040_45 {
s / 12.92
} else {
((s + 0.055) / 1.055).powf(2.4)
}
}
let (r, g, b) = parse_hex(hex).unwrap_or((0, 0, 0));
0.2126 * channel(r) + 0.7152 * channel(g) + 0.0722 * channel(b)
}
pub fn contrast(a: &str, b: &str) -> f64 {
let (la, lb) = (Theme::luminance(a), Theme::luminance(b));
let (hi, lo) = if la >= lb { (la, lb) } else { (lb, la) };
(hi + 0.05) / (lo + 0.05)
}
}
pub fn parse_hex(hex: &str) -> Option<(u8, u8, u8)> {
let s = hex.strip_prefix('#')?;
if s.len() != 6
|| !s
.bytes()
.all(|b| b.is_ascii_hexdigit() && !b.is_ascii_uppercase())
{
return None;
}
let v = u32::from_str_radix(s, 16).ok()?;
Some((
((v >> 16) & 0xff) as u8,
((v >> 8) & 0xff) as u8,
(v & 0xff) as u8,
))
}