CLAUDE.md#
Tips for future agents working in this repo. Patterned after the sibling
surfacefun-interactive project (same numbl + uihtml two-way bridge), but the
figure is a 2D canvas instead of a three.js surface.
Architecture#
app/— React single-file widget (npm run build→app/dist/index.html)*.mat root — scripts users open and run in numblhelpers/— the region/sampling algorithm (make_region,hit_and_run,hit_and_run_general), put on the path by the driver viaaddpath('helpers')numbl-project.json— project metadata;entryis the landing page. Itsfiguresarray declares the editor-less figure view the deploy exposes at#figure/sampler(runshitandrun_demo.m, shows just the interactive figure); the org profile's "live" link points there.
Multi-file layout / addpath#
hitandrun_demo.m is the driver; its first statement is
addpath('helpers') so make_region / hit_and_run resolve from the subdir.
Two constraints make that placement mandatory:
addpath(<literal>)must be a leading statement in the driver, before any other non-comment statement (numbl's static path resolution requires it; the runtime/interpreter path is fine with it too). It also cannot live inside a function file — only the driver. Sohitandrun_sampler.mcan't add the path itself; runhitandrun_demo.m, not the sampler directly.- Plain subdirectories are otherwise invisible to function resolution — only
addpath'd dirs (plus root,+pkg,@Class,private/) are searched.
This works the same in the browser deploy (the worker runs the interpreter,
which honors the runtime addpath).
Data flow#
MATLAB → figure: set uihtml(..., 'Data', struct) or call
sendEventToHTMLSource(src, 'eventName', data)
Figure → MATLAB: call sendToMATLAB('eventName', value) (see bridge.ts);
receive via HTMLEventReceivedFcn
The demo, built in stages (all done)#
- (done) Generate a convex region +
Nhit-and-run samples in the script; show region + samples in the figure. The samples are drawn server-side and sent viaData. - (done) Add controls to the figure panel: a Samples slider, a Resample button, and a New region button.
- (done) Wire those controls to the sampler (two-way bridge).
- (done) Non-convex regions. A non-convex region checkbox switches to a
dumbbell — two convex bulbs joined by a narrow tube (
make_region(false)), sampled byhit_and_run_general; the movie draws the multiple in-region segments a line makes through it. The dumbbell is non-star-shaped and need not contain the origin, so the general sampler seeds itself with an interior point found by rejection. - (done) Local vs. union sampling. A local segment only checkbox (shown
only when non-convex) switches
hit_and_run_generalbetween sampling the union of all in-region segments (default; uniform) and only the segment through the current point (local; non-uniform). It drives both the sampler and the movie overlay (regionSegments'slocalOnly).
The figure → script protocol (each request carries convex, plus local for
non-convex regions):
- Samples slider (on release) / Resample / local toggle →
sendToMATLAB('resample', { n, x, y, convex, local }).x, yare the current region's vertices — the script is stateless, so it samples the region it is given and replies withsendEventToHTMLSource(src, 'samples', { x, y, n }). - New region / non-convex toggle →
sendToMATLAB('newRegion', { n, convex, local }). The script builds a new region of that type and replies with a fullsendEventToHTMLSource(src, 'data', <payload>)(same shape as the initialData, plusconvex).
hitandrun_sampler.m's sample_region(vx, vy, n, convex, local) dispatches to
hit_and_run (convex, JIT) or hit_and_run_general(..., local) (non-convex,
interpreter). The non-convex path can't JIT (sort + point-in-polygon), so
App.tsx caps its N lower via SAMPLE_CHOICES_NONCONVEX. local only applies
to non-convex regions (App.tsx: useLocal = nonConvex && local); toggling it
resamples the same region.
Ideas for the next iteration: show the chain path / burn-in, or animate the walk.
Client-side vs. server-side interactivity#
- Low-latency / view-only (e.g. dot size, zoom): update React state only — instant, no MATLAB round-trip.
- New samples (depends on the RNG and the algorithm):
sendToMATLAB→ script re-runs →sendEventToHTMLSource→ React re-renders.
Key files#
| File | Purpose |
|---|---|
app/src/App.tsx |
Main React component; reads data, hosts the panel, non-convex + local toggles, movie regionSegments |
app/src/render/RegionView.tsx |
2D canvas renderer (region outline + sample dots + movie segments) |
app/src/bridge.ts |
onData / onHostEvent / sendToMATLAB helpers (generic; identical across these projects) |
hitandrun_sampler.m |
Opens the figure, sends data, handles resample/newRegion (figure plumbing: on_event, sample_region, pack_data, pack_samples) |
helpers/make_region.m |
Random region — convex polygon on an ellipse (no convhull, so the auto-running figure view doesn't race the qhull backend load), or make_region(false) for a dumbbell (two disks + narrow tube) |
helpers/hit_and_run.m |
Convex hit-and-run sampler (half-plane chord intersection; JIT) |
helpers/hit_and_run_general.m |
Non-convex hit-and-run sampler (union of all in-region segments, or local = only the one through the current point; interpreter) |
hitandrun_demo.m |
User-facing driver: addpath('helpers') + seed + call the sampler |
Local iteration#
Always rebuild the figure app first — both workflows read the prebuilt
app/dist/index.html:
cd app && npm install && npm run build # produces app/dist/index.html
cd ..
NUMBL below is a local clone of https://github.com/flatironinstitute/numbl
(e.g. ~/src/numbl). Run its CLI with npx tsx $NUMBL/src/cli.ts … — no
global install.
A. Quick run via the CLI (fastest inner loop)#
npx tsx $NUMBL/src/cli.ts run hitandrun_demo.m --plot
--plot opens the figure in a browser and keeps the script's session alive so
figure → script callbacks work (Ctrl+C to stop). Good for iterating on the .m
- app together.
B. Preview the actual GitHub Pages site locally#
This reproduces what the deploy workflow ships
(it calls flatironinstitute/numbl/.github/actions/build-site@main, which is
just cli.ts build-site after building the site-viewer). The numbl in the
deployed site runs the .m in a web worker in the browser, and it keeps a
live uihtml session there — so figure → script callbacks (resample) round-trip
in the deployed site exactly like in the CLI --plot path. (That two-way bridge
is why the workflow builds numbl from main rather than the npm release.)
# one-time, in the numbl clone: build the browser site-viewer
( cd $NUMBL && npm ci && npm run build:site-viewer )
# from this project: bundle project + viewer into _site/
npx tsx $NUMBL/src/cli.ts build-site . --out _site --base /
# serve it (localhost is a secure context, so coi-serviceworker.js can set the
# COOP/COEP headers numbl's WASM needs)
( cd _site && python3 -m http.server 8080 )
# → open http://localhost:8080/
Notes:
--base /is for serving at localhost root. GitHub Pages serves this project at/<repo>/, so the workflow auto-detects--base /hitandrun-interactive/. Content is identical; only asset paths differ._site/anddist/are git-ignored (see.gitignore).- The real target is GitHub Pages, where the same single-file app runs against numbl compiled in the browser.