feat(proxy): add offset param to inset or outset traced shapes

Trace results can now be inset (negative) or outset (positive) by a
pixel amount. Offset the outline and holes in opposite directions with
clipper-lib's miter joins, then recombine with a boolean difference so
holes grow on inset and shrink on outset. Collapsed shapes return an
empty outline; a split outline keeps the largest ring. Document the
parameter and the new dependency.
This commit is contained in:
2026-08-08 15:37:27 +08:00
parent c7f4bd03fd
commit 9094ad58e0
12 changed files with 284 additions and 17 deletions
+9 -4
View File
@@ -164,9 +164,11 @@ CORS failures on common Workshop hosts would break the viewers.
**Decision:** The proxy exposes `GET /trace?url=...`, which fetches an image,
traces it into a vector shape, and returns the result BSON-encoded. Tracing is
configurable: `mode` (`alpha` default, `bw`, `color`) selects how the region is
derived, and `format` (`shape` default, `svg`) selects the response. The
`shape` format returns a parsed `{ outline, holes }` polygon matching
`@tts/mesh`'s `Shape` interface.
derived, `format` (`shape` default, `svg`) selects the response, and `offset`
(optional, in pixels) insets (negative) or outsets (positive) the resulting
shape — used by the token viewer to shave the anti-aliased fringe off a traced
silhouette. The `shape` format returns a parsed `{ outline, holes }` polygon
matching `@tts/mesh`'s `Shape` interface.
**Context:** The user wants to build a mesh from an image (e.g. a token or tile
art) using `@tts/mesh`. vtracer only returns SVG, but the mesh package consumes
@@ -178,4 +180,7 @@ feeding vtracer's `convertPixels`.
**Alternatives considered:** Returning only the raw SVG and parsing in the web
app near `@tts/mesh`; tracing by color only (no alpha). Rejected — server-side
parsing yields a shape the mesh package can consume directly, and alpha-based
tracing (the default) is the common case for token/tile art.
tracing (the default) is the common case for token/tile art. For shape
inset/outset, `clipper-lib` (Angus Johnson's Clipper ported to JS) was chosen
over the `polygon-offset` package because the latter crashes on degenerate
cases (collapse, hole closure) via a bug in its pinned Martinez dependency.