Multi series
The same chart, now with the v2 DSL imgui_implot_boost_v2. The plot
scope here is more than RAII: it is a snapshot-registered container. Each frame
it serializes the plot’s geometry, axis limits, hovered flag, and mouse-plot
position into the dasImgui snapshot rail, keyed by the window + plot identifiers
(PLOT_WIN/WAVES) — which is exactly what the headless tests assert on. The
item wrappers are OR-typed (float | double | int), so one call site handles any
element type, and SetNext* styling colors the next item.
plot(WAVES, (title = "waves", size = float2(-1.0f, 480.0f), flags = ImPlotFlags.None)) {
setup_axes("sample", "value")
setup_axes_limits(0.0lf, 200.0lf, -1.5lf, 1.5lf)
next_line_style(float4(0.30f, 0.70f, 1.00f, 1.00f), 2.0f)
plot_line("sin", g_sin)
plot_line("cos", g_cos)
next_marker_style(ImPlotMarker.Circle, 4.0f)
plot_scatter("samples", g_pts_x, g_pts_y) // int / float / double all work
}
Source: examples/tutorial/multi_series.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
9require math
10
11// =============================================================================
12// TUTORIAL: multi_series — the v2 `plot` snapshot scope + multi-type items.
13//
14// plot(IDENT, (title, size, flags)) { ... } — v2 RAII plot scope that ALSO
15// serializes its per-frame state (geometry, axis limits, hovered,
16// mouse-plot-pos) into the dasImgui snapshot rail, so a test/playwright
17// layer can assert on it. EndPlot can't be skipped.
18// setup_axes / setup_axes_limits — label + fix the visible range.
19// next_line_style / next_marker_style — style the NEXT item.
20// plot_line / plot_scatter / plot_bars — OR-typed: float | double | int.
21//
22// vs v1's `with_plot`, the v2 `plot` is a snapshot-registered container — its
23// state shows up under the IDENT in `imgui_snapshot`. Built on the dasImgui
24// harness, so it runs standalone (windowed), headless (--headless), and live.
25//
26// STANDALONE: daslang.exe modules/dasImguiImplot/examples/tutorial/multi_series.das
27// LIVE: daslang-live modules/dasImguiImplot/examples/tutorial/multi_series.das
28// =============================================================================
29
30var g_ctx : ImPlotContext?
31var g_sin : array<double>
32var g_cos : array<double>
33var g_pts_x : array<double>
34var g_pts_y : array<double>
35var g_bars : array<int>
36
37[export]
38def init() {
39 harness_init("dasImguiImplot — multi_series", 1280, 860)
40 g_ctx = implot::CreateContext()
41 g_sin <- [for (i in range(200)); double(sin(float(i) * 0.05f))]
42 g_cos <- [for (i in range(200)); double(cos(float(i) * 0.05f))]
43 g_pts_x <- [for (i in range(20)); double(i) * 10.0lf]
44 g_pts_y <- [for (i in range(20)); double(sin(float(i) * 0.5f)) * 0.8lf]
45 g_bars <- [for (i in range(10)); i % 5]
46}
47
48[export]
49def update() {
50 if (!harness_begin_frame()) return
51 harness_new_frame()
52
53 SetNextWindowPos(float2(20.0, 20.0), ImGuiCond.Always)
54 SetNextWindowSize(float2(1240.0, 820.0), ImGuiCond.Always)
55 window(PLOT_WIN, (text = "multi series", closable = false,
56 flags = ImGuiWindowFlags.None)) {
57 text("v2 plot scope: snapshot-registered, multi-type items, per-item styling.")
58 plot(WAVES, (title = "waves", size = float2(-1.0f, 480.0f), flags = ImPlotFlags.None)) {
59 setup_axes("sample", "value")
60 setup_axes_limits(0.0lf, 200.0lf, -1.5lf, 1.5lf)
61 next_line_style(float4(0.30f, 0.70f, 1.00f, 1.00f), 2.0f)
62 plot_line("sin", g_sin)
63 plot_line("cos", g_cos)
64 next_marker_style(ImPlotMarker.Circle, 4.0f)
65 plot_scatter("samples", g_pts_x, g_pts_y)
66 }
67 plot(BARS, (title = "bars (int data)", size = float2(-1.0f, 240.0f),
68 flags = ImPlotFlags.None)) {
69 setup_axes("bucket", "count")
70 plot_bars("counts", g_bars)
71 }
72 }
73
74 harness_end_frame()
75}
76
77[export]
78def shutdown() {
79 if (g_ctx != null) {
80 DestroyContext(g_ctx)
81 }
82 harness_shutdown()
83}
84
85[export]
86def main() {
87 init()
88 while (!exit_requested()) {
89 update()
90 }
91 shutdown()
92}
Walkthrough
The plot snapshot scope
plot(IDENT, (title, size, flags)) { ... } is a [container] macro: the bare
IDENT (WAVES) becomes the plot’s identifier in the snapshot tree, nested
under its window (PLOT_WIN/WAVES). Geometry and the ImPlot queries
(GetPlotLimits / IsPlotHovered / GetPlotMousePos) are captured between
BeginPlot and EndPlot — the only window where they are valid — so a test
reads them straight back out of the payload. This is the hook the whole
testing layer is built on.
OR-typed items
plot_line / plot_scatter / plot_bars accept
array<float> | array<double> | array<int> in one signature. The second plot in
the example feeds plot_bars an array<int> directly — no conversion, no
separate overload at the call site.
Per-item styling
next_line_style(col, weight) and next_marker_style(marker, size) set the
appearance of the next item only (ImPlot’s SetNext* model). Call them
immediately before the item you want to style.
Legend toggle
Every item with a label gets a legend entry, and clicking an entry hides or shows
that series — ImPlot handles this for free; the data is still submitted each frame,
it just isn’t drawn. The plot scope serializes the legend into the snapshot — one
entry per series carrying its label, its shown flag, whether it is hovered, and
its clickable rect — so a test or recording can target an entry by name and verify the
toggle landed. The recording above clicks the cos entry to hide the curve and clicks
it again to bring it back, asserting shown flips each time.
The playwright verb legend_toggle(session, "cos") drives this the way a person does:
it moves the cursor onto the entry, waits until the entry actually reports hovered
(wait_for_legend_hover), and only then clicks — so the synthetic press can’t land
before the hover registers. wait_for_series_shown(session, "cos", false) is the gate
that the click took effect.