Bar groups and pie

The two categorical items — the ones whose data points carry a name, not just a number. plot_bar_groups draws several labelled series clustered into groups (a grouped or stacked bar chart); plot_pie_chart splits a whole into named slices. Both take their per-series labels as a plain array<string>: a daslang string array is already laid out as the const char* const[] block ImPlot wants, so the labels pass straight through with no repacking — the same fact dasImgui’s list_box relies on.

// 3 series x 4 groups, flat row-major (values[item*GROUPS + group])
var g_bar_labels <- ["merchant", "consumer", "industrial"]
var g_bar_values <- [10.0lf, 14.0lf,  9.0lf, 12.0lf,
                      8.0lf, 11.0lf, 13.0lf,  7.0lf,
                      6.0lf,  9.0lf, 15.0lf, 10.0lf]
...
plot(BARS, (title = "quarterly volume", size = float2(560.0f, 560.0f), flags = ImPlotFlags.None)) {
    setup_axes("group", "value")
    plot_bar_groups(g_bar_labels, g_bar_values, ITEMS, GROUPS)
}
plot(PIE, (title = "budget split", size = float2(-1.0f, 560.0f), flags = ImPlotFlags.Equal)) {
    setup_axes("", "", ImPlotAxisFlags.NoDecorations, ImPlotAxisFlags.NoDecorations)
    setup_axes_limits(0.0lf, 1.0lf, 0.0lf, 1.0lf, ImPlotCond.Always)
    plot_pie_chart(g_pie_labels, g_pie_values, 0.5lf, 0.5lf, 0.45lf, "%.0f%%")
}

Source: examples/tutorial/bar_groups_and_pie.das.

 1options gen2
 2options _comment_hygiene = true
 3
 4require imgui/imgui_harness
 5require imgui/imgui_containers_builtin
 6require imgui/imgui_widgets_builtin
 7require imgui/imgui_implot_boost_v2
 8require implot
 9
10// =============================================================================
11// TUTORIAL: bar_groups_and_pie — the two categorical / labelled items.
12//
13//   plot_bar_groups(label_ids, values, item_count, group_count)  — item_count series
14//       clustered into group_count groups. `values` is a flat item_count*group_count
15//       row-major matrix (values[item*group_count+group]); `label_ids` names one
16//       series per row (they show up in the legend). The Stacked / Horizontal flags
17//       restack / reorient the bars.
18//   plot_pie_chart(label_ids, values, x, y, radius)              — one slice per
19//       (label, value) pair, centered at data-coords (x, y). Pie plots want square,
20//       decoration-free axes spanning [0,1].
21//
22// Both take label_ids : array<string> — a daslang string array IS already a
23// const char* const[] block, so it passes straight to ImPlot.
24//
25// STANDALONE: daslang.exe modules/dasImguiImplot/examples/tutorial/bar_groups_and_pie.das
26// LIVE:       daslang-live modules/dasImguiImplot/examples/tutorial/bar_groups_and_pie.das
27// =============================================================================
28
29let ITEMS = 3      // series (one label / color each)
30let GROUPS = 4     // clusters along the x-axis
31
32var g_ctx : ImPlotContext?
33var g_bar_labels : array<string>
34var g_bar_values : array<double>     // ITEMS*GROUPS, row-major
35var g_pie_labels : array<string>
36var g_pie_values : array<double>
37
38[export]
39def init() {
40    harness_init("dasImguiImplot — bar_groups_and_pie", 1280, 720)
41    g_ctx = implot::CreateContext()
42
43    // One row of GROUPS values per series, laid out flat (values[item*GROUPS+group]).
44    g_bar_labels <- ["merchant", "consumer", "industrial"]
45    g_bar_values <- [10.0lf, 14.0lf,  9.0lf, 12.0lf,    // merchant
46                      8.0lf, 11.0lf, 13.0lf,  7.0lf,    // consumer
47                      6.0lf,  9.0lf, 15.0lf, 10.0lf]    // industrial
48
49    // One slice per (label, value); values need not be normalized.
50    g_pie_labels <- ["rent", "food", "transit", "savings"]
51    g_pie_values <- [35.0lf, 25.0lf, 18.0lf, 22.0lf]
52}
53
54[export]
55def update() {
56    if (!harness_begin_frame()) return
57    harness_new_frame()
58
59    SetNextWindowPos(float2(20.0, 20.0), ImGuiCond.Always)
60    SetNextWindowSize(float2(1240.0, 680.0), ImGuiCond.Always)
61    window(PLOT_WIN, (text = "bar groups & pie", closable = false,
62                      flags = ImGuiWindowFlags.None)) {
63        text("plot_bar_groups clusters labelled series; plot_pie_chart splits a whole.")
64        plot(BARS, (title = "quarterly volume", size = float2(560.0f, 560.0f),
65                    flags = ImPlotFlags.None)) {
66            setup_axes("group", "value")
67            setup_axes_limits(-0.5lf, double(GROUPS) - 0.5lf, 0.0lf, 18.0lf, ImPlotCond.Always)
68            plot_bar_groups(g_bar_labels, g_bar_values, ITEMS, GROUPS)
69        }
70        same_line()
71        plot(PIE, (title = "budget split", size = float2(-1.0f, 560.0f),
72                   flags = ImPlotFlags.Equal)) {
73            setup_axes("", "", ImPlotAxisFlags.NoDecorations, ImPlotAxisFlags.NoDecorations)
74            setup_axes_limits(0.0lf, 1.0lf, 0.0lf, 1.0lf, ImPlotCond.Always)
75            plot_pie_chart(g_pie_labels, g_pie_values, 0.5lf, 0.5lf, 0.45lf, "%.0f%%")
76        }
77    }
78
79    harness_end_frame()
80}
81
82[export]
83def shutdown() {
84    if (g_ctx != null) {
85        DestroyContext(g_ctx)
86    }
87    harness_shutdown()
88}
89
90[export]
91def main() {
92    init()
93    while (!exit_requested()) {
94        update()
95    }
96    shutdown()
97}

Walkthrough

The recording tours both charts and exercises the grouped bars’ legend with real synthetic input: clicking a series in the legend hides it across every group at once — the whole industrial color drops out of all four quarters — and a second click brings it back. It self-verifies that the series’ shown flag flips both ways and that both plots render, so a no-op toggle or a missing chart fails at teardown.

Bar groups

plot_bar_groups(label_ids, values, item_count, group_count, group_size, shift, flags) plots item_count series across group_count clusters. values is one flat array of item_count * group_count numbers in row-major order — series i’s value for group g is values[i*group_count + g] — and label_ids names one series per row (the names appear in the legend, each in its series color). The groups sit at integer x positions 0 .. group_count-1 by default. Two flags reshape the chart without touching the data: ImPlotBarGroupsFlags.Stacked stacks the series within each group instead of placing them side by side, and ImPlotBarGroupsFlags.Horizontal swaps the bars onto the y-axis.

Pie charts

plot_pie_chart(label_ids, values, x, y, radius, label_fmt, angle0, flags) draws one slice per (label_ids, values) pair, centered at data-coordinates (x, y) with radius in data units. A pie wants a square, decoration-free coordinate space: set ImPlotFlags.Equal on the plot so the circle is not stretched, hide both axes with ImPlotAxisFlags.NoDecorations, and pin the limits to [0,1] x [0,1]. label_fmt is a printf format applied to each slice’s value ("%.0f%%" here) — pass "" to draw no slice labels. Values need not sum to one; the Normalize flag forces a full circle when they sum to less than one.

Coloring

Both items auto-color from the active colormap — series and slices take successive colormap entries, exactly like the auto-colored lines in colormaps and style. Wrap either plot in with_colormap(...) to recolor the whole family, or call next_fill_style(col) before a single plot_bars to override one series.

Testing the categorical items

test_bar_groups_and_pie opens both plots through the playwright layer and asserts each rendered. The bar plot pins its axis limits with ImPlotCond.Always, so wait_for_axis_limits converging proves the scope ran past plot_bar_groups — i.e. the labelled-series item submitted, exercising the array<string>const char* const[] path end to end on the real headless render.