Skip to main content

esoc_chart/
new_theme.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2//! Enhanced theme for the grammar-of-graphics chart API.
3
4use esoc_color::{Color, ColorScale, Palette};
5
6/// Theme controlling visual appearance of grammar-based charts.
7#[derive(Clone, Debug)]
8pub struct NewTheme {
9    /// Background color.
10    pub background: Color,
11    /// Foreground color (text, axes, ticks).
12    pub foreground: Color,
13    /// Muted foreground color (subtitles, secondary text).
14    pub muted_foreground: Color,
15    /// Color palette for data series.
16    pub palette: Palette,
17    /// Grid line color.
18    pub grid_color: Color,
19    /// Grid line width.
20    pub grid_width: f32,
21    /// Whether to show grid lines.
22    pub show_grid: bool,
23    /// Base font size from which all others are derived.
24    pub base_font_size: f32,
25    /// Title font size (base * 1.2).
26    pub title_font_size: f32,
27    /// Subtitle font size (base * 1.1).
28    pub subtitle_font_size: f32,
29    /// Axis label font size (base * 1.0).
30    pub label_font_size: f32,
31    /// Tick label font size (base * 0.9).
32    pub tick_font_size: f32,
33    /// Legend font size (base * 0.9).
34    pub legend_font_size: f32,
35    /// Font family.
36    pub font_family: String,
37    /// Axis line width.
38    pub axis_width: f32,
39    /// Default data line width.
40    pub line_width: f32,
41    /// Default scatter point size (area in px²).
42    pub point_size: f32,
43    /// Optional color scale for heatmaps (default: viridis).
44    pub color_scale: Option<ColorScale>,
45}
46
47impl NewTheme {
48    /// Default light theme.
49    pub fn light() -> Self {
50        let base = 13.0_f32;
51        Self {
52            background: Color::WHITE,
53            foreground: Color::from_srgb8(0x33, 0x33, 0x33), // near-black, softer than pure #000
54            muted_foreground: Color::from_srgb8(0x73, 0x73, 0x73), // sRGB ~0.45
55            palette: Palette::tab10(),
56            grid_color: Color::from_srgb8(0xE0, 0xE0, 0xE0), // sRGB #E0E0E0
57            grid_width: 0.5,
58            show_grid: true,
59            base_font_size: base,
60            title_font_size: (base * 1.2).max(8.0),
61            subtitle_font_size: (base * 1.1).max(8.0),
62            label_font_size: (base * 1.0).max(8.0),
63            tick_font_size: (base * 0.9).max(8.0),
64            legend_font_size: (base * 0.9).max(8.0),
65            font_family: "sans-serif".to_string(),
66            axis_width: 1.0,
67            line_width: 2.0,
68            point_size: 30.0, // px² area (Vega default)
69            color_scale: None,
70        }
71    }
72
73    /// Dark theme.
74    pub fn dark() -> Self {
75        let base = 13.0_f32;
76        Self {
77            background: Color::from_srgb8(0x1e, 0x1e, 0x2e),
78            foreground: Color::from_srgb8(0xcd, 0xd6, 0xf4),
79            muted_foreground: Color::new(0.55, 0.55, 0.65, 1.0), // muted light
80            palette: Palette::tab10(),
81            grid_color: Color::from_srgb8(0x3a, 0x3a, 0x4a), // visible on dark bg
82            grid_width: 0.5,
83            show_grid: true,
84            base_font_size: base,
85            title_font_size: (base * 1.2).max(8.0),
86            subtitle_font_size: (base * 1.1).max(8.0),
87            label_font_size: (base * 1.0).max(8.0),
88            tick_font_size: (base * 0.9).max(8.0),
89            legend_font_size: (base * 0.9).max(8.0),
90            font_family: "sans-serif".to_string(),
91            axis_width: 1.0,
92            line_width: 2.0,
93            point_size: 30.0,
94            color_scale: None,
95        }
96    }
97
98    /// Publication theme — clean, minimal, for papers.
99    pub fn publication() -> Self {
100        let base = 10.0_f32;
101        Self {
102            background: Color::WHITE,
103            foreground: Color::BLACK,
104            muted_foreground: Color::new(0.25, 0.25, 0.25, 1.0),
105            palette: Palette::tab10(),
106            grid_color: Color::TRANSPARENT,
107            grid_width: 0.0,
108            show_grid: false,
109            base_font_size: base,
110            title_font_size: (base * 1.2).max(8.0),
111            subtitle_font_size: (base * 1.1).max(8.0),
112            label_font_size: (base * 1.0).max(8.0),
113            tick_font_size: (base * 0.9).max(8.0),
114            legend_font_size: (base * 0.9).max(8.0),
115            font_family: "serif".to_string(),
116            axis_width: 1.0,
117            line_width: 1.5,
118            point_size: 25.0, // slightly smaller for publication
119            color_scale: None,
120        }
121    }
122}
123
124impl Default for NewTheme {
125    fn default() -> Self {
126        Self::light()
127    }
128}
129
130/// Preferred alias for [`NewTheme`].
131pub type Theme = NewTheme;