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

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.