Skip to content

Bar chart

BarChart (insightdock/bar_chart.h) draws vertical bars per category, either grouped side-by-side or stacked, and inherits the shared axis/grid/legend/title machinery from CartesianChart — see Concepts if you want the internals.

Grouped Stacked
Grouped bar Stacked bar

Minimal example

#include "insightdock/charts.h"

using namespace insightdock::charts;

BarChart bar;
bar.set_categories({"Q1", "Q2", "Q3", "Q4"});
bar.add_series("Revenue", {100, 120, 90, 140});
bar.add_series("Cost", {60, 70, 65, 80});
ChartExporter::save(bar, "bar.png");

Grouped vs. stacked

bar.set_mode(BarMode::Grouped); // default: series drawn side-by-side per category
bar.set_mode(BarMode::Stacked); // series stack vertically per category

Bar-specific options

bar.bar_options() returns a mutable BarOptions& with everything bar-specific — layout, shape, and data labels. The old set_mode()/set_group_gap()/set_bar_gap() methods still work as thin forwarders onto the same struct.

Field Default Purpose
mode BarMode::Grouped Grouped, Stacked, or Stacked100 (each category normalised to 100%, see below).
orientation BarOrientation::Vertical Vertical (default) or Horizontal — see below.
group_gap 0.2 Gap between category groups, as a fraction of slot width, [0, 0.5].
bar_gap 0.1 Gap between bars within a group (grouped mode only), [0, 0.5].
corner_radius 0.0 Rounds bar corners, in pixels. 0 = square (default).
border_color transparent Bar outline color. Transparent (default) draws no border regardless of border_width.
border_width 0.0 Outline stroke width in pixels. A rounded bar's border is a plain rectangle — it slightly overlaps the rounded corners.
data_labels disabled DataLabelOptions — draws each bar's formatted value above it (stacked mode: centered in its segment).
bar.bar_options().corner_radius = 8.0f;
bar.bar_options().border_width = 1.5f;
bar.bar_options().border_color = Color::rgb(30, 90, 180);
bar.bar_options().data_labels.enabled = true;

ComboChart's bar series share this same BarOptions struct via combo.bar_options().

Horizontal orientation

For rankings/leaderboards or category names too long to fit under a vertical x-axis, set orientation to draw bars extending rightward from a category axis on the left instead of upward from one at the bottom:

BarChart bar;
bar.bar_options().orientation = BarOrientation::Horizontal;
bar.set_categories({"North America", "Europe", "Asia-Pacific", "Latin America", "Africa"});
bar.add_series("Revenue", {320, 280, 410, 150, 95});

The left margin widens automatically to fit the longest category label. Grouped and stacked modes both work in horizontal orientation. Not available on ComboChart (its line-series geometry has no horizontal equivalent).

ReferenceLine/ReferenceBand keep their meaning relative to the axes: Horizontal is a constant value (so it is drawn as a vertical line here), Vertical is a category position (drawn as a horizontal line).

100% stacked

bar.set_mode(BarMode::Stacked100);

Every category is normalised so its segments add up to 100%, which shows each series' share instead of its magnitude. The value axis is fixed at 0–100% and is labelled with % automatically (set y_tick_format or custom_y_formatter to override). Negative and missing values count as 0, and a category whose total is not positive is left empty. Data labels still show the original values. Works in horizontal orientation too.

Shared CartesianOptions

Every CartesianChart subclass (BarChart, LineChart, AreaChart, ComboChart) shares the same axis options struct, accessed via cartesian_options():

bar.cartesian_options().y_axis_label = "Amount";
bar.cartesian_options().y_tick_format = TickFormat::SI;    // 1500000 → "1.5M"
bar.cartesian_options().begin_at_zero = true;              // default
bar.cartesian_options().max_x_labels = 12;                 // cap dense category axes
bar.cartesian_options().min_y_labels = bar.cartesian_options().max_y_labels = 12; // ~12 y-ticks

See Usage guide → Tick formatting for the full TickFormat list and custom formatter callbacks, and Concepts for what min_y_labels/max_y_labels do under the hood.

Log y-axis

y_scale_type = ScaleType::Log switches the primary (left) y-axis to a base-10 logarithmic mapping with power-of-10 / 1-2-5-mantissa "nice" ticks — for network traffic, latency, or financial data spanning multiple orders of magnitude, where a linear axis would flatten the low end into an unreadable line hugging the bottom:

chart.cartesian_options().y_scale_type = ScaleType::Log;

begin_at_zero is ignored when the axis is Log (a log domain can't include zero). Works on BarChart, LineChart, AreaChart, and ComboChart's primary axis; the secondary (right) axis stays Linear-only in v1. See examples/log_scale_example.cc.

Continuous DateTime x-axis

LineChart and AreaChart (not BarChart/ComboChart — see below) can position points by real timestamp instead of an evenly-spaced category slot:

chart.set_x_values({1774137600.0, 1774224000.0, 1774483200.0}); // Unix timestamps, any spacing
chart.cartesian_options().x_scale_type = ScaleType::DateTime;
chart.add_series("Data Consumed", {15.87, 22.43, 44.91});

Irregular gaps in the data render as irregular pixel spacing — a real time axis, not category labels that happen to look like dates. Axis ticks are generated from the numeric domain and formatted automatically (YYYY-MM-DD HH:MM). BarChart/ComboChart don't override the hook that enables this (CartesianChart::supports_continuous_x_axis()), so setting x_scale_type on them is a no-op — a fixed-width bar-per-category layout has no well-defined meaning for irregularly- spaced continuous positions. See examples/time_series_line_example.cc.

Reference lines and bands

CartesianOptions::reference_lines/reference_bands draw threshold lines (e.g. an SLA target) or shaded ranges behind the bars — available on every CartesianChart subclass (BarChart, LineChart, AreaChart, ComboChart):

ReferenceLine sla;
sla.orientation = ReferenceOrientation::Horizontal; // default
sla.value = 250;               // a y-axis value for Horizontal
sla.color = Color::rgb(200, 40, 40);
sla.dash = {6.0f, 4.0f};       // default; empty vector = solid
sla.label = "SLA (250ms)";
bar.cartesian_options().reference_lines.push_back(sla);

A Vertical line's value is a fractional category index rather than a category label — category i spans [i, i+1) on that axis, so value = i + 0.5 lands on category i's own center (its tick-label position) and value = i lands exactly on the boundary between category i-1 and category i:

ReferenceLine launch;
launch.orientation = ReferenceOrientation::Vertical;
launch.value = 2.5;            // the 3rd category's center (0-indexed)
launch.color = Color::rgb(30, 140, 30);
launch.label = "Launch";
bar.cartesian_options().reference_lines.push_back(launch);

Not available on PieChart/ScatterChart — they have no draw_overlay() hook to attach to. For anything beyond a straight threshold line/band, draw_overlay is still the extension point — see Concepts → Extending.