Config(simplify=...) / property in Python; simplify option in Node,
with the tolerance documented in .pyi, index.d.ts, and both READMEs.
The Node README's option list also catches up with the clustering
rename and the binary/watershed options, and test.js drops the stale
colorMode key (silently ignored since the rename, so its bw assertion
was testing the default path) and asserts simplify shrinks output.
An alternative region-forming frontend selected by --clustering watershed
(the color_mode field is replaced by clustering: color-cluster | bw |
watershed across CLI, Rust, Python, and Node — it selects the algorithm,
not a color space).
The algorithm is the watershed hierarchy by volume on the 4-adjacency
pixel graph, implemented from the papers:
Cousty, Bertrand, Najman, Couprie, "Watershed Cuts: Minimum Spanning
Forests and the Drop of Water Principle", IEEE TPAMI 31(8), 2009.
Najman, Cousty, Perret, "Playing with Kruskal", ISMM 2013.
Edge weights are the max per-channel color difference between adjacent
pixels (no gradient image); a counting-sorted Kruskal pass builds the
binary partition tree as a flat parents array; a leaves-to-root pass
computes subtree area and volume; each merge's persistence (the volume of
the smaller side, plateau-corrected) becomes its MST edge's saliency; and
cutting the hierarchy is single-linkage over MST edges below the cut
level. Watershed cuts label every pixel — no watershed-line pixel class —
so the output is a strict, gapless partition that drops straight into
both stacked and cutout modes. Integer arithmetic and flat u32 arrays
throughout; deterministic across platforms; ~66 ms on a 1400x775 photo.
The one dial, --watershed-detail (0..=255), maps exponentially to a
target region count (each +25.5 doubles it) since the persistence
distribution is far too skewed for a linear threshold. filter_speckle
absorbs undersized basins into their most color-similar neighbour rather
than dropping them, preserving the partition. The largest region is
emitted first as a solid full-canvas background layer so stacked mode
keeps its seam-free overdraw; the mosaic flatten is unaffected.
Tests: partition invariant (disjoint masks tiling the canvas), detail
monotonicity, min-area absorption, determinism, watershed cases in the
pipeline/golden suites, stacked-vs-cutout interior equivalence, a
watershed seam test, and SegmentKey coverage for the new params.
Publish under the @visioncortex npm org. Add publishConfig.access=public for
the scoped package and update the install/require examples in both READMEs.
New nodejs/ package: a wasm-bindgen crate (vtracer-wasm) built with wasm-pack
that wraps the vtracer framework, plus a thin JS layer for file I/O. Image
decoding (png/jpeg/gif/bmp via the image crate) runs in wasm too, so the
package has zero native dependencies — no sharp, no node-gyp.
No separate general-purpose wasm crate: the Node package directly owns and
wraps the wasm. Excluded from the cargo workspace (wasm-bindgen cdylib), built
with wasm-pack.
JS API (camelCase options): convertBuffer, convertPixels, convertFile,
convertFileSync — each taking an Options object (preset, colorMode,
hierarchical/cutout mosaic, mode, palette, maxColors, optimize, ...). Ships
index.d.ts types and a node smoke test. README updated.
Verified: builds to wasm32-unknown-unknown; `node test.js` passes; output
matches the CLI/Python bindings (253 paths on the tank sample).