concept-collection / barycentric-rational
barycentric-rational / CLAUDE.md
6.3 KBPreviewCodeBlameHistoryRaw

CLAUDE.md#

Notes for future agents working in this repo. See README.md first — it explains the paper and what each tab shows.

The shape of the thing#

A standalone Vite + React app that embeds numbl as a library (numbl/browser, createNumblSession). This is the seqlab / numbl-image-filter pattern, not the numbl-project.json + site-viewer pattern that hitandrun-interactive uses: there is no numbl-project.json here and the deploy is a plain vite build to GitHub Pages.

The MATLAB layer is the interesting part, and it is worth understanding before changing anything.

How a run works#

  1. The editor holds a method script: function definitions only, no statements.
  2. src/engine/files.ts composes main.m = src/matlab/driver.m + "\n" + that script. The script's functions therefore become local functions of the driver, which is the only way the driver can call them.
  3. src/matlab/lib/*.m go into the session as ordinary function files.
  4. Parameters are written to params.json in the session VFS; the driver reads it with jsondecode(fileread(...)) and writes out.json, which the app reads back with session.readFile.

One session per script. Changing a parameter only rewrites params.json and re-runs — a full boot is only needed when the script text changes, because the script is compiled into main.m at boot.

Gotcha: invoke the script with run('main.m'), not main;#

numbl 0.4.18 mis-binds the arguments of a script's local functions when the script is invoked by name from the REPL, which is what session.execute gives us: the callee sees its parameters as undefined (Undefined function or variable 'p'). Reproduce it outside the browser with

npx tsx $NUMBL/src/cli.ts eval "t;"          # fails
npx tsx $NUMBL/src/cli.ts eval "run('t.m');" # works
npx tsx $NUMBL/src/cli.ts run t.m            # works

on any script with a local function that takes an argument. runner.ts goes through run('main.m') for this reason. If numbl fixes it, the workaround is harmless either way.

Gotcha: jsonencode flattens single-row matrices#

jsonencode([1 2 3]) is [1,2,3], not [[1,2,3]], so a matrix with one row arrives on the JavaScript side with the wrong nesting. drv_rows in the driver converts matrices to a cell array of rows before encoding. Use it for anything matrix-shaped (E, orders, P, L).

NaN and ±Inf all cross as null, which is why the TypeScript side uses Num = number | null everywhere and every plot helper skips nulls.

The contract with the method script#

w = bary_weights(x, d)             % required
r = bary_eval(x, y, w, t)          % required
[P, L] = local_blend(x, y, d, t)   % optional; without it the Blending tab is empty

The driver wraps local_blend in try/catch and reports the message, so a script that omits it is not an error. Everything else the tabs show — the denominator, its roots, the real poles, the polynomial and spline overlays — is derived in the driver from x, y and w, which is deliberate: it means the Poles tab tells the truth about whatever weights the user supplies, and is why equal.m and random.m light it up.

Numerical points that took some getting right#

Tests#

npm run test:matlab     # ~2 min: the .m layer vs the paper, through the numbl CLI
npm run test:browser    # ~1 min: the built app in headless Chrome

test:matlab needs a numbl clone (NUMBL_DIR, default ~/src/numbl) and runs main.m directly with the CLI, so it does not exercise the REPL path — the browser test is what covers that. Both check real numbers from the paper rather than snapshots; if you change the MATLAB layer, they will tell you.

test:browser builds nothing itself — run npm run build first. It spawns vite preview detached and kills the process group, because signalling npx alone leaves the server holding the port.

Visual judgement is not something either suite does. Ask a human to look at the page; the headless run is for console errors and behavioural assertions only.

Plotting#

src/plot/ is a small hand-rolled SVG layer, no chart library. palette.ts documents which colour does which job (identity, polarity, ordinal, status) and records the colour-vision validator results; if you add a series, re-validate rather than picking a hex that looks nice. Curves that run off to infinity are clamped to a band just outside the frame and clipped, so a pole leaves the frame in the right direction instead of producing unusable path data.

Ideas not done#