Realtime scroll

A streaming chart: each frame appends a sample to a capped buffer and re-pins the x axis to a window that ends at “now”, so the plot scrolls as data arrives. The load-bearing detail is the condition on the axis limits — ImPlotCond.Always re-applies the range every frame, where the default ImPlotCond.Once would set it on the first frame and then let the user pan.

g_t += DT                                // advance the clock
g_xs |> push(g_t); g_fast |> push(...)   // append one sample
if (length(g_xs) > MAX_POINTS) {         // drop the oldest -> fixed-width window
    g_xs |> erase(0); g_fast |> erase(0)
}
plot(SCROLL, (title = "scrolling signals", size = float2(-1.0f, 600.0f), flags = ImPlotFlags.None)) {
    setup_axes("time (s)", "value")
    setup_axes_limits(g_t - HISTORY, g_t, -1.2lf, 1.2lf, ImPlotCond.Always)
    plot_line("fast", g_xs, g_fast)
}

Source: examples/tutorial/realtime_scroll.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
 8require math
 9
10// =============================================================================
11// TUTORIAL: realtime_scroll — a rolling buffer plotted against a moving x window.
12//
13//   Each frame appends a sample to a capped buffer (oldest dropped past MAX_POINTS)
14//   and re-pins the x axis to [t - HISTORY, t] with ImPlotCond.Always, so the plot
15//   scrolls as data streams in. The y axis stays locked.
16//
17//   setup_axes_limits(..., ImPlotCond.Always)  — re-apply the range EVERY frame
18//                                                 (vs Once, which lets the user pan).
19//
20// STANDALONE: daslang.exe modules/dasImguiImplot/examples/tutorial/realtime_scroll.das
21// LIVE:       daslang-live modules/dasImguiImplot/examples/tutorial/realtime_scroll.das
22// =============================================================================
23
24let HISTORY = 5.0lf     // seconds of data kept on screen
25let DT = 0.1lf          // sim seconds advanced per frame
26let MAX_POINTS = 70     // buffer cap (~ HISTORY / DT + margin)
27
28var g_ctx : ImPlotContext?
29var g_t : double = 0.0lf
30var g_xs : array<double>
31var g_fast : array<double>
32var g_slow : array<double>
33
34[export]
35def init() {
36    harness_init("dasImguiImplot — realtime_scroll", 1100, 720)
37    g_ctx = implot::CreateContext()
38}
39
40[export]
41def update() {
42    if (!harness_begin_frame()) return
43    harness_new_frame()
44
45    // Advance the clock and append one sample of each signal.
46    g_t += DT
47    g_xs |> push(g_t)
48    g_fast |> push(double(sin(float(g_t) * 2.0f)))
49    g_slow |> push(double(sin(float(g_t) * 2.0f + 1.5f)) * 0.6lf)
50    // Drop the oldest sample once the buffer is full — a fixed-width rolling window.
51    if (length(g_xs) > MAX_POINTS) {
52        g_xs |> erase(0)
53        g_fast |> erase(0)
54        g_slow |> erase(0)
55    }
56
57    SetNextWindowPos(float2(20.0, 20.0), ImGuiCond.Always)
58    SetNextWindowSize(float2(1060.0, 680.0), ImGuiCond.Always)
59    window(PLOT_WIN, (text = "realtime", closable = false,
60                      flags = ImGuiWindowFlags.None)) {
61        text("A rolling buffer: each frame appends a sample and the x axis is pinned to [t-HISTORY, t].")
62        plot(SCROLL, (title = "scrolling signals", size = float2(-1.0f, 600.0f),
63                      flags = ImPlotFlags.None)) {
64            setup_axes("time (s)", "value")
65            // Re-pin the x window every frame so it scrolls; lock y.
66            setup_axes_limits(g_t - HISTORY, g_t, -1.2lf, 1.2lf, ImPlotCond.Always)
67            plot_line("fast", g_xs, g_fast)
68            plot_line("slow", g_xs, g_slow)
69        }
70    }
71
72    harness_end_frame()
73}
74
75[export]
76def shutdown() {
77    if (g_ctx != null) {
78        DestroyContext(g_ctx)
79    }
80    harness_shutdown()
81}
82
83[export]
84def main() {
85    init()
86    while (!exit_requested()) {
87        update()
88    }
89    shutdown()
90}

Walkthrough

There is nothing to drive here — the chart streams on its own. The recording narrates over the live animation and asserts the window genuinely advances (x_min climbs over the narration), so a frozen plot would fail it.

The rolling buffer

The buffer is three parallel array<double> (one x, two y) trimmed together: push appends the new sample, and once the length exceeds MAX_POINTS an erase(0) drops the oldest. MAX_POINTS is sized at roughly HISTORY / DT so the buffer holds exactly the visible window.

Pinning the x window

setup_axes_limits(g_t - HISTORY, g_t, …, ImPlotCond.Always) keeps the x range a fixed HISTORY-wide window whose right edge is the current time g_t. Because g_t advances every frame, the left edge climbs — which is what the test_realtime_scroll regression checks: it waits for x_min to pass 0.5, proving the window is genuinely scrolling rather than static.