Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
15 changes: 15 additions & 0 deletions doc/source/tutorials/heatmap_histogram.rst
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,21 @@ Source: ``examples/tutorial/heatmap_histogram.das``.
:language: das
:linenos:

Walkthrough
===========

.. video:: heatmap_histogram.mp4

A guided tour of the two statistical items. The cursor sweeps diagonally across the
heatmap — each cell takes its color from its value through the active colormap, so
the structure in the data shows up as a pattern of colors — then glides to the
histogram, which bins a flat sample array into bars whose shape is the distribution.
There is no interaction to teach beyond the shapes; the recording self-verifies that
both plots render *and* that the synthetic cursor genuinely lands hovered over the
grid, so a dead frame or a missed cursor fails at teardown. A *sequential* colormap
that runs a smooth ramp — the natural fit for a heatmap — is shown in
:ref:`colormaps and style <tutorial_implot_colormaps_and_style>`.

Heatmaps
========

Expand Down
81 changes: 81 additions & 0 deletions tests/integration/record_heatmap_histogram.das
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
options gen2
options indenting = 4
options no_unused_block_arguments = false
options no_unused_function_arguments = false

require imgui/imgui_implot_app public
require imgui/imgui_implot_playwright public
require daslib/json public
require daslib/json_boost public

//! Driver: record heatmap_histogram.apng - the two statistical 2D / distribution items,
//! a display tour. On the left a HEAT plot (plot_heatmap maps a rows x cols grid through
//! the active colormap); on the right a HIST plot (histogram bins a flat sample array into
//! a 1D distribution). No interaction to teach here beyond the shapes - and colormap
//! scaling belongs to colormaps_and_style - so the cursor traces across the grid then
//! glides to the histogram while the voice names each item. Self-verifying with REAL synth
//! input: both plots must render at every beat, and the cursor must actually land hovered
//! over the heatmap grid, so a dead frame or a missed cursor aborts at teardown. The
Comment on lines +16 to +18
//! headless regression is test_heatmap_histogram.das.

let HEAT = "PLOT_WIN/HEAT"
let HIST = "PLOT_WIN/HIST"

// Hold a beat's caption for the remainder of its voice dwell, measuring real elapsed
// work (glide + verify) so the next say_begin can't fire early and overlap the voice.
def hold_remainder(dwell : uint; t0 : int64) {
let elapsed = uint(get_time_usec(t0) / 1000)
if (dwell > elapsed) {
sleep(dwell - elapsed)
}
}

[export]
def main {
with_implot_recording_app("examples/tutorial/heatmap_histogram.das", "heatmap_histogram.apng", 45) $(app) {
let heat = implot_open(app, HEAT)
let hist = implot_open(app, HIST)
var snap = wait_for_widget(app, HEAT, 15.0f)
if (snap == null) {
panic("{HEAT} never rendered - wrong app running?")
}
// HEAT data area geometry - maps screen fractions to a glide across the grid.
let p = plot_payload(snap, heat)
let hx = p?["pos"]?["x"] ?? 0.0f
let hy = p?["pos"]?["y"] ?? 0.0f
let hw = p?["plot_size"]?["x"] ?? 0.0f
let hh = p?["plot_size"]?["y"] ?? 0.0f

// ---- Beat 1: both statistical plots ----
move_to(app, plot_center(snap, heat), 700)
wait_for_mouse_idle(app)
record_check_rendered(app, HEAT, true)
record_check_rendered(app, HIST, true)
say(app, "two statistical plots", HEAT,
[voice = "Two statistical plots. On the left, a heatmap: a grid of cells colored by value. On the right, a histogram of a sample set."])

// ---- Beat 2: trace the heatmap grid ----
let d_heat = say_begin(app, "a heatmap colors a grid by value", HEAT,
[voice = "The heatmap draws a rows by columns grid, and each cell takes its color from its value through the active colormap, so the structure in the data shows up as a pattern of colors."])
let t_heat = ref_time_ticks()
// Glide diagonally across the grid so the cursor sweeps the gradient.
move_to(app, (hx + hw * 0.1f, hy + hh * 0.15f), 300)
wait_for_mouse_idle(app)
move_to(app, (hx + hw * 0.9f, hy + hh * 0.85f), 1200)
wait_for_mouse_idle(app)
record_check(app, "the cursor lands hovered over the heatmap grid",
wait_for_hovered(heat, true) != null)
record_check_rendered(app, HEAT, true)
hold_remainder(d_heat, t_heat)

// ---- Beat 3: the histogram ----
let d_hist = say_begin(app, "a histogram bins a sample set", HIST,
[voice = "The histogram bins a flat array of samples into bars, showing the shape of the distribution. Here, a count per bin, with the bin count picked automatically from the sample size."])
let t_hist = ref_time_ticks()
var gsnap = snapshot(app)
move_to(app, plot_center(gsnap, hist), 900)
wait_for_mouse_idle(app)
record_check_rendered(app, HIST, true)
hold_remainder(d_hist, t_hist)
}
}
Loading