A splice slice with very uneven point spacing — a few-pixel jog followed
by a long straight leg, which the walker produces around thin strands —
was fitted by a single cubic that interpolated every sample exactly while
swinging up to ~30 px sideways between them (the fit error was only
measured at the samples). On the Cityscape sample at -p 8 -g 28 the
maroon strand's wall bulged across its 2 px gap; a long-standing defect
present in pre-1.0 vtracer as well.
The fix lives in visioncortex 0.9.1 (densify slices with witness points,
keep the full multi-cubic chain): the mosaic's shared-boundary spline
fitter switches to the new fit_points_with_beziers so open segments get
the same treatment as stacked mode. Verified against the exact pixel-mode
rasterization — the fitted wall now matches it at every probed row — with
identical path counts and only +3 cubics across the whole Cityscape
output. All three workspaces (core, py, nodejs) carry a [patch.crates-io]
entry pointing at the local visioncortex until 0.9.1 is published.
Regression coverage for spline handle anchoring; re-bless goldens
tests/spline_fit.rs traces the real reproduction (the Cityscape sample at
color precision 8 / gradient step 28, whose maroon region contains a 1 px
by 330 px strand) through both compositors and asserts the property the
sparse-slice bug violated: every cubic's control points stay within its
shape's on-curve bounding box plus a 15 px allowance. The ballooning fit
put a handle 25 px outside the whole shape, so the margin is decisive in
both directions. The sample photo is decoded via a test-only image
dev-dependency.
The synthetic goldens are re-blessed: the fitting fix shifts fixture
coordinates slightly (witness points refine fits even on clean shapes);
the old files still passed the render-diff, so the visual change is
sub-tolerance, but goldens should byte-match intentional behavior.
Depend on published visioncortex 0.9.1; drop local path patches
Feature — mosaic spline segment fitter (crates/vtracer/src/mosaic/fit.rs):
open-path cubic fitting for boundary segments, reusing the now-public
visioncortex primitives (PathSimplify::limit_penalties for symmetric,
gap-free staircase removal; open-path SubdivideSmooth::{find_corners,
subdivide_keep_corners,find_splice_points}; fit_points_with_bezier per splice
slice). Matches stacked spline curve quality; endpoints pinned to lattice
nodes so shared boundaries stay seam-free.
Fix — stacked mode punched holes in cluster masks (to_image_with_hole .. true);
stacked must trace solid layers and occlude by paint-order overdraw (false).
Holes left the layer below exposed as hairline seams.
Fix — the relative SVG writer measured a subpath's opening `m` from the last
vertex instead of the subpath start (SVG resets the current point to the start
after Z), misplacing holes / extra subpaths at optimize=1/2.
Tests — new tests/equivalence.rs: stacked-vs-mosaic interior agreement (all
fitters) and a seam guard (a full-coverage image must render fully opaque).
svg round-trip test (absolute vs relative encode identical geometry). mosaic
spline endpoint-pinning test. Regenerated goldens; added disc_mosaic_spline.
resvg added as a dev-dependency (test-only; not compiled for wasm).
Drop unused MosaicOptions placeholder
The strict/seam-stroke mitigations aren't needed — the mosaic geometry is
already gapless and seam-free. Remove the no-op MosaicOptions struct and thread
it out of Compositing::Mosaic and compose_mosaic.
12 synthetic-image cases covering every stage: all three fitters, holes,
region adjacency, hierarchical layering, binary mode, fixed-palette and
auto-quantize color fitting, and the three optimizer/writer levels.
Fixtures are built from in-code images, not the JPEG samples, because JPEG
decoding is image-crate-version dependent and would make goldens fragile.
Regenerate after an intentional change with VTRACER_BLESS=1.