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