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:
@@ -86,6 +86,7 @@ apps/web ──► apps/proxy ──► packages/tts ──► packages/shared
|
||||
| `@hono/cors` | CORS middleware | `apps/proxy` |
|
||||
| `bson` | BSON deserialization of TTS save files | `packages/tts` |
|
||||
| `@visioncortex/vtracer` | Raster-to-SVG vectorization (wasm) | `apps/proxy` |
|
||||
| `clipper-lib` | Polygon offsetting (inset/outset) for traced shapes | `apps/proxy` |
|
||||
| `sharp` | Image decoding to RGBA | `apps/proxy` |
|
||||
| `svgpath` | SVG path parsing for traced shapes | `apps/proxy` |
|
||||
| `cheerio` | Workshop browse page scraping | `apps/proxy` |
|
||||
@@ -101,9 +102,9 @@ apps/web ──► apps/proxy ──► packages/tts ──► packages/shared
|
||||
must stay isomorphic.
|
||||
- **`bson` is Node-only** — used by the fetcher and the trace route, not the
|
||||
analysis layer.
|
||||
- **`@visioncortex/vtracer`, `sharp`, `svgpath` are backend-only** — the trace
|
||||
route lives in `apps/proxy`; they never appear in `packages/extract`, which
|
||||
must stay isomorphic.
|
||||
- **`@visioncortex/vtracer`, `sharp`, `svgpath`, `clipper-lib` are
|
||||
backend-only** — the trace route lives in `apps/proxy`; they never appear in
|
||||
`packages/extract`, which must stay isomorphic.
|
||||
- **`packages/extract` has zero external runtime deps** — it relies only on
|
||||
platform `fetch` / `Blob`, keeping it portable to a future frontend.
|
||||
|
||||
|
||||
+9
-4
@@ -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.
|
||||
@@ -241,16 +241,23 @@ Hono server exposing search + fetch.
|
||||
headers, which would block three.js loaders in the browser; routing through
|
||||
the proxy makes those assets loadable. Only `http(s)` URLs are allowed.
|
||||
- `routes/trace.ts`
|
||||
- `GET /trace?url=...&mode=alpha&threshold=128&format=shape` — fetch an
|
||||
image, trace it into a vector shape, and return the result BSON-encoded.
|
||||
`mode` is `alpha` (default), `bw`, or `color`; `format` is `shape`
|
||||
(default) or `svg`. `shape` returns a parsed `{ outline, holes }` polygon
|
||||
matching `@tts/mesh`'s `Shape` interface, ready to extrude.
|
||||
- `GET /trace?url=...&mode=alpha&threshold=128&format=shape&offset=...` —
|
||||
fetch an image, trace it into a vector shape, and return the result
|
||||
BSON-encoded. `mode` is `alpha` (default), `bw`, or `color`; `format` is
|
||||
`shape` (default) or `svg`; `offset` (optional) insets (negative) or
|
||||
outsets (positive) the shape in pixels. `shape` returns a parsed
|
||||
`{ outline, holes }` polygon matching `@tts/mesh`'s `Shape` interface,
|
||||
ready to extrude.
|
||||
- `routes/svgShape.ts`
|
||||
- `parseSvgShape(svg)` — parse a vtracer SVG into a `TracedShape`
|
||||
(`{ outline, holes }`): flatten beziers to polylines, split subpaths into
|
||||
rings, classify by winding (CCW outline / CW hole), and assign holes to
|
||||
their containing outline.
|
||||
- `offsetShape(shape, delta)` — inset/outset a `TracedShape` via
|
||||
`clipper-lib` (Clipper miter joins); outline and holes offset in opposite
|
||||
directions and are recombined 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.
|
||||
- `routes/health.ts`
|
||||
- `GET /health` — liveness.
|
||||
- `env.ts` — zod validation of `STEAM_API_KEY`, `PORT`.
|
||||
@@ -309,7 +316,7 @@ proxy API and `packages/extract` directly for analysis.
|
||||
| GET | `/items/:id` | Full parsed `TTSMod` (`?fileUrl=` skips key) | key* |
|
||||
| GET | `/items/:id/file` | Raw save bytes, filename from header | key* |
|
||||
| GET | `/asset?url=` | CORS-safe proxy for external assets | — |
|
||||
| GET | `/trace?url=&mode=&format=` | Trace an image into a vector shape (BSON) | — |
|
||||
| GET | `/trace?url=&mode=&format=&offset=` | Trace an image into a vector shape (BSON) | — |
|
||||
|
||||
\* `STEAM_API_KEY` is optional; `/items/*` works without it when a `fileUrl`
|
||||
query param is supplied.
|
||||
|
||||
Reference in New Issue
Block a user