One-core-engine P4.3/P4.4: the input/output pipeline processing and the curve
catalog now live in the C++/WASM core (nisps/pipeline/*, nisps/core/math.hpp).
The TS ports are deleted and the browser drives the WASM chains.
Engine:
- WasmIML owns a nisps_pipeline_create handle + bridge buffers and exposes
setInputConfig (TS InputConfig → 15-float wire), processInput, resetInput,
setOutputConfig (Infinity slew → 0), setOutputFreezeMask, processOutput
(in place), resetOutput, curveApply, curveApplyBatch (chunked). Handle +
buffers created in init_, freed in dispose, output-sized buffers realloc'd
on reshape.
- Spine routes setInputs through iml.processInput/processOutput (state lives
C++-side); config source-of-truth stays TS-side and is pushed on attach /
setInputConfig / setOutputConfig. Preserves ?debug=1 fixed-dt determinism
(same dt fed to the WASM calls). EngineApi gains setInputConfig/
setOutputConfig/curveApply/curveApplyBatch.
- New types-only modules: pipeline-types.ts (InputConfig/OutputConfig +
defaults + wire int mappers) and curve-catalog.ts (CurveName + name→id).
types.ts declares the pipeline/curve C ABI. engine barrel updated.
- DELETED src/engine/{input-pipeline,output-pipeline,curves}.ts.
Tests (P4.4 gate — recorded-gesture regression):
- pipeline-golden.test.ts now loads the built WASM (indirect-eval shim,
tests/wasm-load.ts) and drives the frozen gesture/output fixtures through the
C++ chains, honouring the per-event dt clock contract. Tolerance 1e-5
(non-momentum drift <5e-7). The 3 momentum configs carry 1e-2: proven-inherent
f32 drift (a byte-faithful f32 port of the exact original algorithm matches
the WASM to <6e-8 while both diverge from the f64 capture by ~7-9e-3), NOT a
core bug.
- curves-golden.json: linear/square/sqrt/centered_power kept as the original
f64 captures (C++ matches within <3e-8); exp/log/sigmoid/cubic RE-BASELINED
from the WASM (deliberate switch to firmware-exact k=1 exp/log, slope-6
sigmoid, true cubic x^3). Provenance recorded in-file.
- _generate.ts rebuilt as the WASM curve re-baseline tool; pipeline-golden-lib
trimmed to pure data builders.
Docs: fixtures/README.md + manifold/ONBOARDING.md updated.
Gates: typecheck, bun test (9), vite build, playwright e2e (27) all green.
100 lines
4.3 KiB
TypeScript
100 lines
4.3 KiB
TypeScript
/**
|
|
* Fixture (re)generator. Run from manifold/: `bun tests/fixtures/_generate.ts`
|
|
*
|
|
* SCOPE SINCE ONE-CORE-ENGINE P4 (2026-07-18):
|
|
* - gesture-trace.json — pure deterministic data (regenerated identically).
|
|
* - curves-golden.json — the exp/log/sigmoid/cubic entries are RE-BASELINED
|
|
* from the canonical C++/WASM core (nisps/core/math.hpp); the
|
|
* linear/square/sqrt/centered_power entries are PRESERVED as their original
|
|
* 2026-07-13 f64 TS captures (the C++ core matches them within 1e-5, so
|
|
* there is no behaviour change to record). Provenance is embedded.
|
|
*
|
|
* The input-pipeline-golden.json / output-pipeline-golden.json fixtures are a
|
|
* FROZEN pre-migration capture — this tool does NOT rewrite them (there is no
|
|
* TS pipeline left to capture from; the regression is proven by driving the
|
|
* WASM chains against the frozen goldens in pipeline-golden.test.ts).
|
|
*/
|
|
|
|
import { readFileSync, writeFileSync } from 'node:fs';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { dirname, join } from 'node:path';
|
|
|
|
import { CURVE_ID, CURVE_NAMES, CURVE_DEFAULT_PARAMS, type CurveName } from '../../src/engine/curve-catalog';
|
|
import { INPUT_DT_MS, CURVE_SAMPLE_COUNT, buildGestureTrace } from '../pipeline-golden-lib';
|
|
import { loadPipelineWasm } from '../wasm-load';
|
|
|
|
const DIR = dirname(fileURLToPath(import.meta.url));
|
|
const write = (name: string, data: unknown) => {
|
|
writeFileSync(join(DIR, name), JSON.stringify(data, null, 2) + '\n');
|
|
console.log('wrote', name);
|
|
};
|
|
|
|
/** Curves whose maths deliberately changed at the P4 migration. */
|
|
const REBASELINED: ReadonlyArray<CurveName> = ['exp', 'log', 'sigmoid', 'cubic'];
|
|
const UNCHANGED: ReadonlyArray<CurveName> = ['linear', 'square', 'sqrt', 'centered_power'];
|
|
|
|
async function main(): Promise<void> {
|
|
const wasm = await loadPipelineWasm();
|
|
|
|
// 1. Gesture trace (pure data) ---------------------------------------------
|
|
const trace = buildGestureTrace();
|
|
write('gesture-trace.json', {
|
|
description: 'Canonical synthetic pointer trace over the input pipeline native [0,1]^2 domain.',
|
|
captured: '2026-07-13',
|
|
note: 'Pure deterministic data; the input chain (WASM) is driven over it in pipeline-golden.test.ts.',
|
|
dt_ms: INPUT_DT_MS,
|
|
count: trace.length,
|
|
domain: { x: [0, 1], y: [0, 1] },
|
|
segments: ['h-sweep', 'v-sweep', 'diagonal', 'spiral', 'figure-eight', 'dwell+jumps'],
|
|
events: trace,
|
|
});
|
|
|
|
// 2. Curves — preserve the unchanged f64 entries, re-baseline the 4 changed --
|
|
const existing = JSON.parse(
|
|
readFileSync(join(DIR, 'curves-golden.json'), 'utf8'),
|
|
) as { curves: Record<string, number[]> };
|
|
|
|
const curves: Record<string, number[]> = {};
|
|
for (const name of CURVE_NAMES) {
|
|
if (UNCHANGED.includes(name)) {
|
|
curves[name] = existing.curves[name]!; // keep the original f64 capture
|
|
continue;
|
|
}
|
|
const id = CURVE_ID[name];
|
|
const param = name === 'centered_power' ? 1.0 : 0;
|
|
const out: number[] = [];
|
|
for (let i = 0; i < CURVE_SAMPLE_COUNT; i++) {
|
|
out.push(wasm.curveApply(id, i / (CURVE_SAMPLE_COUNT - 1), param));
|
|
}
|
|
curves[name] = out;
|
|
}
|
|
|
|
write('curves-golden.json', {
|
|
description:
|
|
'Curve catalog sampled at 129 points x in [0,1] inclusive. linear/square/sqrt/centered_power are the original 2026-07-13 f64 TS captures (behaviour unchanged); exp/log/sigmoid/cubic RE-BASELINED from the canonical C++/WASM core (nisps/core/math.hpp) on the 2026-07-18 P4 migration — see provenance and README.',
|
|
captured: '2026-07-13',
|
|
sampleCount: CURVE_SAMPLE_COUNT,
|
|
xStep: 1 / (CURVE_SAMPLE_COUNT - 1),
|
|
defaultParams: CURVE_DEFAULT_PARAMS,
|
|
provenance: {
|
|
unchanged: {
|
|
curves: UNCHANGED,
|
|
source: 'TS f64 capture 2026-07-13; C++ core matches within 1e-5 (no behaviour change).',
|
|
},
|
|
rebaselined: {
|
|
curves: REBASELINED,
|
|
date: '2026-07-18',
|
|
source:
|
|
'nisps/core/math.hpp via nisps_curve_apply (WASM). The old TS curves.ts used k=4 exp/log, slope-8 sigmoid, smoothstep cubic; the canonical catalog uses k=1-normalised exp/log, slope-6 sigmoid, true cubic x^3. The browser now adopts the firmware-exact behaviour.',
|
|
},
|
|
},
|
|
curves,
|
|
});
|
|
|
|
console.log('NOTE: input/output pipeline goldens are frozen; not regenerated.');
|
|
}
|
|
|
|
main().catch((err) => {
|
|
console.error('[_generate] error:', err);
|
|
process.exit(1);
|
|
});
|