Skip to main content

plotters_statistical/figures/
colorbar.rs

1//! A vertical colorbar drawn directly onto a drawing area, shared by the
2//! heatmap figures.
3
4use plotters::coord::Shift;
5use plotters::prelude::*;
6use plotters::style::text_anchor::{HPos, Pos, VPos};
7
8use crate::colormap::{GradientColorMap, Normalization};
9
10/// Draw a vertical colorbar filling the left edge of `area`, with a handful of
11/// tick labels to its right. `steps` controls the gradient resolution.
12pub fn draw_colorbar<DB: DrawingBackend>(
13    area: &DrawingArea<DB, Shift>,
14    colormap: &GradientColorMap,
15    norm: &Normalization,
16    ticks: usize,
17) -> Result<(), Box<dyn std::error::Error>>
18where
19    DB::ErrorType: 'static,
20{
21    let (w, h) = area.dim_in_pixel();
22    let (w, h) = (w as i32, h as i32);
23    let bar_w = 16;
24    let top = 8;
25    let bottom = h - 8;
26    let bar_h = (bottom - top).max(1);
27    let steps = bar_h.max(2);
28
29    // Gradient strip: one thin rect per pixel row, t = 1 at the top.
30    for s in 0..steps {
31        let y0 = top + (bar_h * s) / steps;
32        let y1 = top + (bar_h * (s + 1)) / steps;
33        let t = 1.0 - s as f64 / (steps - 1) as f64;
34        let color = colormap.color(t);
35        area.draw(&Rectangle::new([(4, y0), (4 + bar_w, y1)], color.filled()))?;
36    }
37    // Outline.
38    area.draw(&Rectangle::new(
39        [(4, top), (4 + bar_w, bottom)],
40        BLACK.stroke_width(1),
41    ))?;
42
43    // Tick labels.
44    let ticks = ticks.max(2);
45    let label_style = TextStyle::from(("sans-serif", 12).into_font())
46        .color(&BLACK)
47        .pos(Pos::new(HPos::Left, VPos::Center));
48    for k in 0..ticks {
49        let t = k as f64 / (ticks - 1) as f64;
50        let y = bottom - ((bar_h * k as i32) / (ticks as i32 - 1));
51        let value = norm.value(t);
52        let _ = w; // width is available if callers extend this later
53        area.draw(&Text::new(
54            format!("{value:.2}"),
55            (4 + bar_w + 6, y),
56            label_style.clone(),
57        ))?;
58    }
59    Ok(())
60}