Skip to content

figma-transformer: decode raw vectorNetwork blobs + compose boolean-op vectors to SVG (#247)#275

Merged
chubes4 merged 1 commit into
trunkfrom
cook/figma-vectornetwork-decode
Jun 28, 2026
Merged

figma-transformer: decode raw vectorNetwork blobs + compose boolean-op vectors to SVG (#247)#275
chubes4 merged 1 commit into
trunkfrom
cook/figma-vectornetwork-decode

Conversation

@chubes4

@chubes4 chubes4 commented Jun 28, 2026

Copy link
Copy Markdown
Contributor

What

Closes the dominant remaining .fig fidelity gap in the figma-transformer package: raw Figma vectorNetworkBlob geometry and BOOLEAN_OPERATION nodes, plus a vector-success undercount in the diagnostics summary. Tracking issue: #247.

Data motivation

On a fixture-matrix run over real .fig designs (David Perell), the transform left placeholders with reasons unsupported_vector_network_blob (24) and unsupported_boolean_operation_children (~14). Separately, the matrix/quality summary reported generated_svg_count: 0 / vector_placeholders: 38 even though the result artifact showed 141 paths actually rendered — the summary was reading the externalized-SVG-file count, not the rendered-path count.

Changes

vectorNetwork → SVG (ScenegraphNormalizer)

  • New general decoder parses the raw vectorNetwork buffer: header counts, a 20-byte vertex table (x/y), a segment table (start/end vertex + optional bezier tangents), and a per-region list of directed segment loops.
  • Straight segments emit L; segments with non-zero tangents emit a cubic C using vertex + tangent control points. Region winding rule (NONZERO/EVENODD) is carried through to fill-rule.
  • Segment stride is auto-detected (16 bytes without tangents, 24 with) by requiring a fully consistent parse — every index in range, each region a continuous closed loop, and the region table consuming the buffer exactly — so a wrong layout guess is rejected rather than emitted as garbage geometry.
  • Runs as a fallback after the existing exact-match decoders, so legacy/synthetic shapes are unchanged. Reuses the existing Kiwi/blob plumbing (readUint32/readFloatPair/vectorNetworkCounts).

Boolean operations (StaticHtmlEmitter)

  • BOOLEAN_OPERATION nodes are composed from their child vector geometry into one <svg>. UNION (and unknown ops) overlay the child paths — visually correct for the common icon case. SUBTRACT/INTERSECT/EXCLUDE approximate hole-cutting with a single fill-rule:evenodd path when children share the operation origin; otherwise they fall back to the overlay. data-figma-boolean-operation records the op.
  • booleanOperation is carried through the Kiwi field policy so real files get the correct op type.

Diagnostics undercount fix (FigmaTransformer + StaticHtmlEmitter)

  • The single-page and multi-page (matrix) quality summaries now set generated_svg_count from actual rendered_paths, with the externalized-asset count exposed separately as externalized_svg_asset_count.
  • Added vector_network_decoded and boolean_operations_composed outcome counts across the vectors aggregate, decode coverage, and both summary surfaces; the multi-page summary now also carries vector_nodes / vector_decoded_to_svg / vector_decode_coverage_ratio.

Tests

Added focused contract coverage in tests/contract/run.php:

  • a synthetic curved vectorNetwork (3 vertices, one curved segment, one region) decodes to a real <svg><path d="M0 0L10 0C…Z"> (not a placeholder) and is counted as network-decoded by both the coverage diagnostic and the summary rollup;
  • a UNION BOOLEAN_OPERATION composes two child paths into one svg;
  • a SUBTRACT BOOLEAN_OPERATION produces a single fill-rule:evenodd composite.

cd figma-transformer && php tests/contract/run.php → "Figma Transformer contract tests passed." Synthetic fixtures only; the large local .fig files were not run.

Honest caveats

  • The 24-byte tangent-bearing segment layout and the per-region winding field are validated against synthetic fixtures and the proven 16-byte layout, not against a real curved .fig blob in this session (large local fixtures were intentionally not run). The auto-detection requires an exactly-consuming, index-valid parse, so a wrong layout is rejected to a placeholder rather than mis-rendered — but blobs whose real layout differs from this model will still fall back honestly.
  • Region decoding handles one loop per region (multiple regions → multiple subpaths with the region winding rule). Multi-loop-per-region byte layouts are not yet modeled and fall back.
  • True path-boolean geometry is not computed; SUBTRACT/INTERSECT/EXCLUDE across children at different origins fall back to overlay (recorded in the op attribute), and only same-origin children get the evenodd composite.

AI assistance

)

Vector decode is the dominant remaining .fig fidelity gap. On a real fixture
matrix run (David Perell) the transform left placeholders with reasons
unsupported_vector_network_blob (24) and unsupported_boolean_operation_children
(~14), and the quality/matrix summary undercounted vector success
(generated_svg_count: 0 while 141 paths actually rendered).

ScenegraphNormalizer: add a general vectorNetwork blob decoder that parses the
raw buffer (header counts, 20-byte vertex table, segment table, per-region
directed segment loops) into real SVG paths. Straight segments emit L; segments
carrying bezier tangents emit a cubic C from vertex+tangent control points.
Segment stride (16 without tangents, 24 with) is auto-detected by requiring a
fully consistent parse that consumes the buffer exactly and references only
valid indices, so a wrong layout guess is rejected rather than emitted as
garbage. Region winding rule is carried through to fill-rule. This runs as a
fallback after the existing exact-match decoders, so legacy shapes are
unchanged.

StaticHtmlEmitter: compose BOOLEAN_OPERATION nodes from child vector geometry
into one <svg>. UNION (and unknown) overlay the child paths (visually correct
for the common icon case); SUBTRACT/INTERSECT/EXCLUDE approximate hole-cutting
with a single fill-rule:evenodd path when children share the operation origin,
otherwise fall back to the overlay. booleanOperation is carried through the
Kiwi field policy.

Diagnostics: wire the quality/matrix summary rollup to reflect actual
rendered_paths so generated_svg_count stops reporting 0 when paths were emitted;
expose the externalized-asset count separately as externalized_svg_asset_count;
surface vector_network_decoded and boolean_operations_composed outcome counts in
the vectors aggregate, decode coverage, single-page summary, and the multi-page
matrix summary.

Tests: add contract coverage for a curved vectorNetwork blob decoding to a real
cubic-curve path (counted as network-decoded by both the coverage diagnostic and
the summary rollup), a UNION boolean op composing two child paths, and a
SUBTRACT boolean op producing an evenodd composite.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@chubes4 chubes4 merged commit 6cb76dc into trunk Jun 28, 2026
@chubes4 chubes4 deleted the cook/figma-vectornetwork-decode branch June 28, 2026 12:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant