concept-collection / hitandrun-interactive
hitandrun-interactive
Go to fileHistoryFork
.githubInteractive hit-and-run sampling of a 2D convex region
appNon-convex: add local-segment vs. union sampling mode
helpersNon-convex region: replace star polygon with a two-bulb dumbbell
.gitignoreInteractive hit-and-run sampling of a 2D convex region
.numblignoreInteractive hit-and-run sampling of a 2D convex region
CLAUDE.mdNon-convex region: replace star polygon with a two-bulb dumbbell
hitandrun_demo.mInteractive hit-and-run sampling of a 2D convex region
hitandrun_sampler.mNon-convex: add local-segment vs. union sampling mode
numbl-project.jsonAdd figure-only view (#figure/sampler)
README.mdNon-convex region: replace star polygon with a two-bulb dumbbell

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 N is 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), and hit_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:

  • Convexhit_and_run.m takes 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 large N instant. The %!numbl:assert_jit directive 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-convexhit_and_run_general.m finds 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 and N is 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.