Skip to main content

plotters_statistical/
style.rs

1//! Shared styling: a color-blind-safe default palette and small helpers for
2//! building [`ShapeStyle`]s, used by every chart type so the crate's output is
3//! visually consistent.
4//!
5//! The default palette is the **Okabe–Ito** qualitative palette, designed to be
6//! distinguishable under the common forms of color-vision deficiency.
7//!
8//! > Okabe, M. & Ito, K. (2008). *Color Universal Design (CUD): How to make
9//! > figures and presentations that are friendly to colorblind people.*
10//! > <https://jfly.uni-koeln.de/color/>
11
12use plotters::style::{Color, RGBAColor, RGBColor, ShapeStyle};
13
14/// The eight Okabe–Ito colors as `(r, g, b)` triples, in the recommended order.
15///
16/// Index 0 is black; [`palette_color`] skips it by default so cycling starts on
17/// a hue, but it is available here for callers who want the full set.
18pub const OKABE_ITO: [(u8, u8, u8); 8] = [
19    (0, 0, 0),       // black
20    (230, 159, 0),   // orange
21    (86, 180, 233),  // sky blue
22    (0, 158, 115),   // bluish green
23    (240, 228, 66),  // yellow
24    (0, 114, 178),   // blue
25    (213, 94, 0),    // vermillion
26    (204, 121, 167), // reddish purple
27];
28
29/// Default label / axis font family used across the crate's examples.
30pub const DEFAULT_FONT: &str = "sans-serif";
31
32/// Default font size (points) for legend and annotation text.
33pub const DEFAULT_FONT_SIZE: u32 = 16;
34
35/// Returns the `i`-th palette color, cycling through the seven non-black
36/// Okabe–Ito hues. Deterministic, so the *k*-th series always gets the same
37/// color across every chart type.
38///
39/// ```
40/// use plotters_statistical::style::palette_color;
41/// use plotters::style::Color;
42/// // Cycling wraps after seven hues: index 0 and index 7 map to the same color.
43/// assert_eq!(palette_color(0).to_rgba(), palette_color(7).to_rgba());
44/// ```
45pub fn palette_color(i: usize) -> RGBColor {
46    // Skip index 0 (black) so multi-series charts start on a hue.
47    let (r, g, b) = OKABE_ITO[1 + (i % (OKABE_ITO.len() - 1))];
48    RGBColor(r, g, b)
49}
50
51/// A solid-fill [`ShapeStyle`] of the given color (`filled = true`).
52pub fn fill_style<C: Color>(color: &C) -> ShapeStyle {
53    ShapeStyle {
54        color: color.to_rgba(),
55        filled: true,
56        stroke_width: 0,
57    }
58}
59
60/// A stroked (outline) [`ShapeStyle`] of the given color and width
61/// (`filled = false`).
62pub fn stroke_style<C: Color>(color: &C, width: u32) -> ShapeStyle {
63    ShapeStyle {
64        color: color.to_rgba(),
65        filled: false,
66        stroke_width: width,
67    }
68}
69
70/// A translucent fill built from `color` at the given `alpha` (0.0–1.0).
71///
72/// Used for area shading (AUC fills, violin bodies, box interiors) where an
73/// opaque fill would hide gridlines or overlapping series.
74pub fn translucent_fill<C: Color>(color: &C, alpha: f64) -> ShapeStyle {
75    ShapeStyle {
76        color: color.mix(alpha),
77        filled: true,
78        stroke_width: 0,
79    }
80}
81
82/// Convenience: a fully-specified [`RGBAColor`] with explicit alpha.
83pub fn rgba(r: u8, g: u8, b: u8, alpha: f64) -> RGBAColor {
84    RGBColor(r, g, b).mix(alpha)
85}