Skip to main content

model/
default.rs

1//! Reasonable default builders for a figgy chart.
2//!
3//! The `Default` trait is intentionally not implemented for the chart types;
4//! values that carry user intent (titles, data ranges) must go through builders
5//! like `Chart::with_title`. This module only provides visual / layout defaults
6//! that work for any chart. (Sole exceptions: `DrawStyle` / `SketchOptions` —
7//! pure visual parameters with no user intent, and `serde(default)` requires
8//! the impls.)
9//!
10//! Included: visual style (colors, widths, font sizes, tick lengths, grid),
11//! axis visibility policy (all axis lines and inside ticks on; top/right
12//! labels and titles off, bottom/left labels and titles on), units / formatting
13//! (Linear, Decimal, 3 sig digits), and margins.
14//!
15//! Not included (caller supplies): chart and axis text (empty segments —
16//! filled in by builders), axis range (0..1 placeholder updated by
17//! `Chart::set_*_range` or `auto_fit_*`), and chart_area pixel size (set to
18//! match the host viewport).
19
20use crate::color::Color;
21use crate::colormap::ColorMap;
22use crate::config::{
23    AxisOptions, AxisScale, AxisTitleOptions, BarAlign, ChartTitleOptions, ColorBarOptions, Config,
24    DrawStyle, GridOptions, LabelStyle, Legend, LegendCorner, TickVisibility,
25};
26use crate::format::LabelFormat;
27use crate::layout::{ChartArea, Rect, Side};
28use crate::line::LineStylePreset;
29use crate::text::{RichText, TextAlign};
30
31/// Empty RichText (no segments). Filled in by builders.
32pub fn default_rich_text() -> RichText {
33    RichText {
34        segments: Vec::new(),
35        color: Color::BLACK,
36        font_size: 12.0,
37        font: String::new(),
38        align: TextAlign::Left,
39    }
40}
41
42/// Chart title style. Text starts empty and is filled in by a builder.
43pub fn default_chart_title_options() -> ChartTitleOptions {
44    ChartTitleOptions {
45        text: RichText {
46            segments: Vec::new(),
47            color: Color::BLACK,
48            font_size: 28.0,
49            font: String::new(),
50            align: TextAlign::Left,
51        },
52        visible: true,
53        offset_x: 0.0,
54        offset_y: 0.0,
55        // 28pt title text + a bit of breathing room above and below.
56        top_margin: 32.0,
57    }
58}
59
60/// Axis title style. Text starts empty and is filled in by a builder.
61pub fn default_axis_title_options() -> AxisTitleOptions {
62    AxisTitleOptions {
63        text: RichText {
64            segments: Vec::new(),
65            color: Color::BLACK,
66            font_size: 22.0,
67            font: String::new(),
68            align: TextAlign::Left,
69        },
70        visible: true,
71        offset_x: 0.0,
72        offset_y: 0.0,
73    }
74}
75
76/// Tick label style (X axis).
77pub fn default_label_style_x() -> LabelStyle {
78    LabelStyle {
79        visible: true,
80        color: Color::BLACK,
81        font_size: 18.0,
82        label_visible: true,
83        label_font: String::new(),
84        label_offset_x: 0.0,
85        label_offset_y: 0.0,
86        format: LabelFormat::Decimal,
87        significant_digits: 3,
88    }
89}
90
91/// Tick label style (Y axis). Currently identical to X.
92pub fn default_label_style_y() -> LabelStyle {
93    default_label_style_x()
94}
95
96/// X axis options. Range is a 0..1 placeholder — replace with
97/// `Chart::set_x_range` or `auto_fit_x`.
98pub fn default_axis_options_x() -> AxisOptions {
99    AxisOptions {
100        scale: AxisScale::Linear,
101        min: 0.0,
102        max: 1.0,
103        major_spacing: 0.2,
104        minor_count: 4,
105        inverted: false,
106        label_style: default_label_style_x(),
107        tick: TickVisibility::Inside,
108        title_option: default_axis_title_options(),
109        out_margin: 80.0,
110        line_offset: 0.0,
111        line_visible: true,
112        line_color: Color::BLACK,
113        line_width: 1.0,
114        line_style: LineStylePreset::Solid,
115        major_tick_length: 5.0,
116        minor_tick_length: 3.0,
117    }
118}
119
120/// Y axis options. Range is a 0..1 placeholder — replace with
121/// `Chart::set_y_range` or `auto_fit_y`.
122pub fn default_axis_options_y() -> AxisOptions {
123    AxisOptions {
124        scale: AxisScale::Linear,
125        min: 0.0,
126        max: 1.0,
127        major_spacing: 0.2,
128        minor_count: 4,
129        inverted: false,
130        label_style: default_label_style_y(),
131        tick: TickVisibility::Inside,
132        title_option: default_axis_title_options(),
133        out_margin: 110.0,
134        line_offset: 0.0,
135        line_visible: true,
136        line_color: Color::BLACK,
137        line_width: 1.0,
138        line_style: LineStylePreset::Solid,
139        major_tick_length: 5.0,
140        minor_tick_length: 3.0,
141    }
142}
143
144/// The colourbar's z axis. Range is a 0..1 placeholder — replace with the
145/// matrix's z statistics.
146///
147/// Differs from the chart axes in two places, both because the strip is not a
148/// data area: `line_visible` is off (the strip's own border draws that edge, and
149/// a second line on top of it is just a thicker border), and ticks point
150/// `Outside` so they sit in the label margin instead of over the colours.
151pub fn default_axis_options_colorbar() -> AxisOptions {
152    let mut title_option = default_axis_title_options();
153    // A z title is the exception, not the rule — most colourbars are labelled
154    // by their tick values alone.
155    title_option.visible = false;
156    AxisOptions {
157        scale: AxisScale::Linear,
158        min: 0.0,
159        max: 1.0,
160        major_spacing: 0.2,
161        minor_count: 4,
162        inverted: false,
163        label_style: default_label_style_y(),
164        tick: TickVisibility::Outside,
165        title_option,
166        // Holds the tick labels only; no title band by default.
167        out_margin: 60.0,
168        line_offset: 0.0,
169        line_visible: false,
170        line_color: Color::BLACK,
171        line_width: 1.0,
172        line_style: LineStylePreset::Solid,
173        major_tick_length: 5.0,
174        minor_tick_length: 3.0,
175    }
176}
177
178/// Colourbar defaults — visible, on the right, three quarters of the data
179/// area's height, Viridis. `nan_color` is fully transparent: a value the ramp
180/// cannot place reads as absent rather than as some particular colour.
181///
182/// Not `Default::default()` on purpose, following this module: `axis.min` /
183/// `axis.max` carry user (or data) intent and the placeholder range is only
184/// meaningful as a starting point a caller replaces.
185pub fn default_colorbar_options() -> ColorBarOptions {
186    ColorBarOptions {
187        visible: true,
188        side: Side::Right,
189        thickness_px: 18.0,
190        gap_px: 24.0,
191        length_frac: 0.75,
192        align: BarAlign::Center,
193        offset_x: 0.0,
194        offset_y: 0.0,
195        colormap: ColorMap::Viridis,
196        nan_color: Color::from_rgba(0.0, 0.0, 0.0, 0.0),
197        border_color: Color::from_rgb8(80, 80, 80),
198        border_width: 1.0,
199        axis: default_axis_options_colorbar(),
200    }
201}
202
203/// Major grid on, minor grid off. Light gray lines.
204pub fn default_grid_options() -> GridOptions {
205    GridOptions {
206        show_major_x: true,
207        major_x_color: Color::from_rgb8(200, 200, 200),
208        major_x_width: 1.0,
209        major_x_style: LineStylePreset::Solid,
210
211        show_major_y: true,
212        major_y_color: Color::from_rgb8(200, 200, 200),
213        major_y_width: 1.0,
214        major_y_style: LineStylePreset::Solid,
215
216        show_minor_x: false,
217        minor_x_color: Color::from_rgb8(230, 230, 230),
218        minor_x_width: 0.5,
219        minor_x_style: LineStylePreset::Dot,
220
221        show_minor_y: false,
222        minor_y_color: Color::from_rgb8(230, 230, 230),
223        minor_y_width: 0.5,
224        minor_y_style: LineStylePreset::Dot,
225    }
226}
227
228/// Legend defaults — hidden, top-right corner, standard padding. Content
229/// starts empty; its font/font_size are live and consumed at draw time.
230pub fn default_legend() -> Legend {
231    Legend {
232        visible: false,
233        content: RichText {
234            segments: Vec::new(),
235            color: Color::BLACK,
236            font_size: 14.0,
237            font: String::new(),
238            align: TextAlign::Left,
239        },
240        corner: LegendCorner::TopRight,
241        offset_x: 0.0,
242        offset_y: 0.0,
243        padding: 8.0,
244        bg_color: Color {
245            r: 1.0,
246            g: 1.0,
247            b: 1.0,
248            a: 0.85,
249        },
250        border_color: Color {
251            r: 0.6,
252            g: 0.6,
253            b: 0.6,
254            a: 1.0,
255        },
256    }
257}
258
259/// chart_area placeholder — caller resizes to match the host viewport.
260pub fn default_chart_area() -> ChartArea {
261    ChartArea(Rect {
262        x: 0,
263        y: 0,
264        width: 1000,
265        height: 800,
266    })
267}
268
269/// Reasonable default Config.
270///
271/// - `bottom_x` / `left_y`: axis line, ticks, labels, and title all on. Text
272///   is empty — fill in via `Chart::with_x_title` / `with_y_title`.
273/// - `top_x` / `right_y`: axis line and inside ticks on (kept for the frame);
274///   labels and axis title off. Enable those for special charts (e.g. dual-axis).
275/// - `chart_title`: title band reserved; text empty until `Chart::with_title`.
276pub fn default_config() -> Config {
277    let mut top_x = default_axis_options_x();
278    top_x.label_style.label_visible = false;
279    top_x.title_option.visible = false;
280    // Labels/title off → out_margin only needs to span the gap between the
281    // axis line and the title band.
282    top_x.out_margin = 8.0;
283
284    let mut right_y = default_axis_options_y();
285    right_y.label_style.label_visible = false;
286    right_y.title_option.visible = false;
287    right_y.out_margin = 8.0;
288
289    Config {
290        chart_area: default_chart_area(),
291        top_x,
292        bottom_x: default_axis_options_x(),
293        left_y: default_axis_options_y(),
294        right_y,
295        chart_title: default_chart_title_options(),
296        grid: default_grid_options(),
297        legend: default_legend(),
298        picked_points: None,
299        picked_data: None,
300        // No z dimension until a field series needs one — see
301        // `default_colorbar_options`.
302        colorbar: None,
303        // Precise mode. Stylized modes are opt-in (`DrawStyle::Sketch(..)`).
304        draw_style: DrawStyle::Precise,
305    }
306}