Interactive hit-and-run sampling#
Runs in the browser via numbl — no install.
🎛 Just the figure →#
The figure-only view runs the sampler and drops you straight into the interactive figure (it shows the run's output first, then the figure). The full developer view — file tree, editable code, console — is below.
▶ Open hitandrun_demo.m and click Run#
A random 2D region is generated and N points are drawn uniformly from it by
hit-and-run: from the current point, pick a random direction, take the chord
where that line crosses the region, and jump to a uniform point on it. Repeat.
The figure shows the region and the samples. The region is convex by default,
but you can switch to a non-convex dumbbell — two convex bulbs joined by a
narrow tube — where a single line can enter and leave the region more than once.
Controls:
- Samples — set
N(re-runs the sampler). - Resample — new samples, same region.
- New region — a fresh region (of the current type).
- non-convex region — toggle between a convex region and the non-convex
dumbbell. The non-convex region uses the slower general sampler, so
Nis capped lower. - local segment only (non-convex only) — by default the step samples across every segment where the line crosses the region (the standard walk, uniform on the whole region); check this to instead sample only within the segment that contains the current point — a local walk that can't jump across the gap between two bulbs, so it does not sample uniformly.
- Play movie — step through the algorithm: each step draws the chord — one segment for a convex region, possibly several for a non-convex one (or just the local segment) — and the point that landed on it.
How it works#
hitandrun_demo.m— driver:addpath('helpers'), seed, call the sampler.hitandrun_sampler.m— opens the figure, sends data, handles resample requests, and dispatches to the convex or non-convex sampler.helpers/—make_region.m(convex ellipse or dumbbell region),hit_and_run.m(convex chord), andhit_and_run_general.m(arbitrary simple polygon).app/— a single-file React app that draws the region and samples on a canvas.
The script and figure talk both ways: the script sends the region + samples via
uihtml(..., 'Data', ...), and the controls call back with
sendToMATLAB('resample' | 'newRegion', ...) (each carrying whether the region
is convex and, for non-convex, the local sampling mode), which re-runs the
sampler and returns new points via sendEventToHTMLSource. The script is
stateless — the figure owns the region and passes it back with each request.
Convex vs. non-convex#
hitandrun_sampler.m picks the sampler by region type:
- Convex —
hit_and_run.mtakes the single chord where the line crosses the region (an inward-half-plane intersection). Its loop runs once per sample, so numbl JS-JIT-compiles it to JavaScript — about 30× faster than its interpreter, which is what keeps largeNinstant. The%!numbl:assert_jitdirective asserts this happens (it errors rather than silently falling back). It relies on numbl's scalar-rand()JIT support;rng(seed)still controls the shared PRNG. - Non-convex —
hit_and_run_general.mfinds every crossing along the line and keeps the in-region segments (a point-in-polygon test on each interval's midpoint), so concavities are handled correctly. It then samples in one of two modes: union (default) picks a point uniformly across all those segments, which samples the whole region uniformly; local (the local segment only checkbox) restricts to the single segment straddling the current point — a local walk that can't jump the gap between the two bulbs, so it does not sample uniformly. The sort + polygon tests don't JIT, so this runs in the interpreter andNis capped lower.make_region(false)builds the dumbbell, which is not star-shaped and need not contain the origin, so the sampler finds an interior start by rejection rather than assuming one.
Deploy#
Pushing to main builds the app and publishes the project to GitHub Pages via
the deploy workflow.