Waffle Chart

A waffle chart encodes proportions as colored cells in a rectangular grid. Where a pie chart encodes data as angles, a waffle chart encodes it as area — making it easier to estimate percentages at a glance, especially at multiples of 5% on a 10×10 grid.

Waffle charts are popular in infographic and policy contexts where the audience may find pie slices hard to compare, and they pair naturally with a unit label like "■ = 100 people" to communicate absolute counts.

Import path: kuva::plot::waffle::{WafflePlot, FillOrder, CellShape}


Basic usage

Add categories with .with_category(label, value, color). Values are proportional — only their relative sizes matter. The grid is filled using Largest Remainder (Hamilton) rounding so the total filled cells always equals exactly rows × cols.

#![allow(unused)]
fn main() {
use kuva::plot::waffle::{WafflePlot, FillOrder, CellShape};
use kuva::backend::svg::SvgBackend;
use kuva::render::render::render_multiple;
use kuva::render::layout::Layout;
use kuva::render::plots::Plot;

let waffle = WafflePlot::new()
    .with_category("Treated",   45.0, "#2196F3")
    .with_category("Partial",   30.0, "#FF9800")
    .with_category("Untreated", 25.0, "#F44336")
    .with_legend("Status")
    .with_show_percents();

let plots = vec![Plot::Waffle(waffle)];
let layout = Layout::auto_from_plots(&plots).with_title("Treatment Coverage");
let svg = SvgBackend.render_scene(&render_multiple(plots, layout));
std::fs::write("waffle.svg", svg).unwrap();
}
Basic waffle chart with three categories

Grid size and aspect ratio

The default is a 10×10 grid (100 cells), which cleanly maps 1 cell per percent. Adjust with .with_grid(rows, cols) for different aspect ratios.

#![allow(unused)]
fn main() {
use kuva::plot::waffle::WafflePlot;
use kuva::render::plots::Plot;
use kuva::render::layout::Layout;
use kuva::render::render::render_multiple;

// 5 rows × 20 cols = 100 cells, wide aspect ratio
let waffle = WafflePlot::new()
    .with_grid(5, 20)
    .with_category("Agree",        52.0, "#4CAF50")
    .with_category("Neutral",      21.0, "#9E9E9E")
    .with_category("Disagree",     27.0, "#F44336")
    .with_legend("Response")
    .with_show_percents();

let plots = vec![Plot::Waffle(waffle)];
}
Wide-aspect waffle chart (5×20 grid)

Circle cells

Use .with_shape(CellShape::Circle) for a bubbly, more modern infographic style.

#![allow(unused)]
fn main() {
use kuva::plot::waffle::{WafflePlot, CellShape};
use kuva::render::plots::Plot;
use kuva::render::layout::Layout;
use kuva::render::render::render_multiple;
use kuva::backend::svg::SvgBackend;

let waffle = WafflePlot::new()
    .with_shape(CellShape::Circle)
    .with_gap(0.15)
    .with_category("Yes",     63.0, "#2ca02c")
    .with_category("No",      37.0, "#d62728")
    .with_legend("Vote")
    .with_show_percents();

let plots = vec![Plot::Waffle(waffle)];
let layout = Layout::auto_from_plots(&plots).with_title("Survey Result");
let svg = SvgBackend.render_scene(&render_multiple(plots, layout));
}
Waffle chart with circular cells

Fill direction

FillOrder controls where the first cell is placed and in which direction the grid fills. Use RowMajorBottomLeft to fill upward like a progress bar, or ColMajorTopLeft for column-first layout.

#![allow(unused)]
fn main() {
use kuva::plot::waffle::{WafflePlot, FillOrder};
use kuva::render::plots::Plot;
use kuva::render::layout::Layout;
use kuva::render::render::render_multiple;

// Bottom-up fill — reads like a "filled" progress bar
let waffle = WafflePlot::new()
    .with_fill_order(FillOrder::RowMajorBottomLeft)
    .with_category("Complete", 68.0, "#1f77b4")
    .with_category("Remaining", 32.0, "#aec7e8")
    .with_legend("Progress")
    .with_show_percents();

let plots = vec![Plot::Waffle(waffle)];
}

Unit label and absolute counts

When each cell represents a fixed number, add a .with_unit_label() annotation below the grid and .with_show_counts() to append cell counts to legend entries.

#![allow(unused)]
fn main() {
use kuva::plot::waffle::WafflePlot;
use kuva::render::plots::Plot;
use kuva::render::layout::Layout;
use kuva::render::render::render_multiple;
use kuva::backend::svg::SvgBackend;

// A population of 10,000: each cell = 100 people
let waffle = WafflePlot::new()
    .with_category("Vaccinated",    7800.0, "#2ca02c")
    .with_category("Unvaccinated",  2200.0, "#d62728")
    .with_legend("Status")
    .with_show_percents()
    .with_show_counts()
    .with_unit_label("■ = 100 people");

let plots = vec![Plot::Waffle(waffle)];
let layout = Layout::auto_from_plots(&plots).with_title("Vaccination Coverage (n = 10,000)");
let svg = SvgBackend.render_scene(&render_multiple(plots, layout));
}
Waffle chart with unit annotation and count legend

WafflePlot API reference

WafflePlot builders

MethodDefaultDescription
WafflePlot::new()10×10 grid, square cells, row-major top-left, 10% gap
.with_category(label, value, color)Add a proportional category
.with_categories(iter)Add multiple (label, value, color) at once
.with_grid(rows, cols)10, 10Set grid dimensions
.with_rows(n)10Set number of rows
.with_cols(n)10Set number of columns
.with_gap(f)0.1Gap between cells as fraction of cell size
.with_fill_order(FillOrder)RowMajorTopLeftFill direction and starting corner
.with_shape(CellShape)SquareCell shape: Square or Circle
.with_empty_color(css)"#e8e8e8"Color for unfilled background cells
.with_legend(label)Attach a legend (one entry per category)
.with_show_percents()Append (xx.x%) to legend entries
.with_show_counts()Append (N cells) to legend entries
.with_unit_label(s)Annotation below the grid (e.g. "■ = 100 people")

FillOrder variants

VariantDescription
RowMajorTopLeftLeft-to-right, top-to-bottom (reading order, default)
RowMajorBottomLeftLeft-to-right, bottom-to-top (progress bar style)
ColMajorTopLeftTop-to-bottom, left-to-right (column first)
ColMajorBottomLeftBottom-to-top, left-to-right

CellShape variants

VariantDescription
SquareFilled rectangle (default)
CircleFilled circle inscribed in the cell