// Bubble chart: an XY scatter whose third dimension maps to dot area.
///|
/// Render a bubble chart as a standalone SVG document string. Each entry of
/// `data` is `(x, y, size)`; bubble *area* is proportional to `size`, with the
/// largest bubble drawn at `max_radius` pixels. `title`, `width`, `height`
/// and `theme` are optional.
pub fn bubble_chart(
data : Array[(Double, Double, Double)],
max_radius? : Double = 22.0,
title? : String = "",
width? : Double = 520.0,
height? : Double = 340.0,
theme? : Theme = Theme::light(),
) -> String {
let left = 48.0
let right = 16.0
let top = if title == "" { 16.0 } else { 40.0 }
let bottom = 40.0
let plot_w = width - left - right
let plot_h = height - top - bottom
let xs = data.map(fn(p) { p.0 })
let ys = data.map(fn(p) { p.1 })
let (xlo_raw, xhi_raw) = extent(xs)
let (ylo_raw, yhi_raw) = extent(ys)
let (axis_lo, axis_hi, ticks) = nice_ticks(ylo_raw, yhi_raw, 5)
let (xlo, xhi, x_ticks) = nice_ticks(xlo_raw, xhi_raw, 6)
// Area-true scaling: radius grows with the square root of size.
let mut size_max = 0.0
for p in data {
if p.2 > size_max {
size_max = p.2
}
}
let body = StringBuilder::new()
body.write_string(background_rect(theme, width, height))
body.write_string(
y_axis(theme, left, top, plot_w, plot_h, axis_lo, axis_hi, ticks),
)
body.write_string(x_axis(theme, left, top, plot_w, plot_h, xlo, xhi, x_ticks))
for i in 0..