Skip to content

Examples + textureAtlas refactor: split, move pure math to core, per-renderer browser glue#22

Merged
apresmoi merged 109 commits into
mainfrom
feat/examples
May 22, 2026
Merged

Examples + textureAtlas refactor: split, move pure math to core, per-renderer browser glue#22
apresmoi merged 109 commits into
mainfrom
feat/examples

Conversation

@apresmoi
Copy link
Copy Markdown
Collaborator

Summary

Two big workstreams converged on this branch:

Examples

Four Vite apps under examples/{html,vanilla,react,vue} demonstrating the minimal usage for each renderer. Each is a workspace member using workspace:^ so local changes flow through immediately. Six examples per app: baked-shapes, multi-mesh, solid-glb, textured-glb, animated, voxel (html skips animated).

textureAtlas refactor + dep graph cleanup

  • Split the 5000-line polycss/render/textureAtlas.ts into ~16 focused files under polycss/render/atlas/.
  • Moved pure math to corepackages/core/src/atlas/ (matrix, plan, strategy, packing, paint, edge-repair, etc.). Core keeps lib: ["ES2020"] — no DOM types added.
  • Severed React/Vue → polycss dep. React and Vue now depend only on @layoutit/polycss-core. Each renderer owns its own copy of the canvas atlas pipeline + browser detection (~700 lines duplicated per renderer, deliberate trade for clean dep boundaries).
  • Tests co-located next to source. Per-renderer test coverage so drift between the three browser-glue copies surfaces immediately.

Bug fixes along the way

  • Apple hero rotation, gallery camera-handle migration, /cottage.glb → polycss.com paths.
  • Z-up cylinder/cone/torus, double matrix3d wrap, projective dispatch, Vue mtl background-shorthand interaction.
  • mergePolygons always returned the merged result (was guarded by length-equality which silently dropped merged polygons when count matched).
  • React + Vue baked-shapes flicker — diagnosed with Playwright + MutationObserver (bench/flicker-diagnose.mjs). Root cause was missing will-change: transform on .polycss-scene in React/Vue stylesheets, causing Chromium to re-rasterize every leaf per frame.

Coverage

Package Lines
@layoutit/polycss-core 74.55%
@layoutit/polycss 81.32%
@layoutit/polycss-react 79.04%
@layoutit/polycss-vue 78.93%

Core/atlas folder up from ~30% to 85%+ across the moved files. Total: 2219 tests across 4 packages, all green.

AGENTS.md updates

Updated the monorepo table (React/Vue depend only on core), added the examples workspace row, and added a new "Renderer-owned browser glue" section to the cross-package discipline rules — a fix to the canvas pipeline or styles.ts now needs to land in all three renderers in the same PR.

Test plan

  • `pnpm test` — 2219 tests pass across 4 packages
  • `pnpm build:packages` — all 4 packages build clean (CJS + ESM + DTS)
  • `pnpm build:website` — clean
  • Manual: each of the four example apps renders correctly (html/vanilla/react/vue, six examples each)
  • Manual: gallery loads, Primitives bucket shows all 11 primitives
  • Manual: landing apple auto-rotates without flicker

apresmoi added 30 commits May 21, 2026 02:12
Exports isFullRectSolid, buildTextureEdgeRepairSets, computeTextureAtlasPlanPublic,
getSolidPaintDefaultsFromPlans, formatMatrix3d, formatCssLengthPx,
formatSolidQuadEntryMatrix, formatBorderShapeEntryMatrix, isBorderShapeSupported,
isSolidTriangleSupported, filterAtlasPlans, packTextureAtlasPlansWithScale,
buildAtlasPages, isSolidTrianglePlan, isProjectiveQuadPlan, and related types so
React/Vue can import pure logic instead of duplicating it.
… polycss

Removes ~2000 lines of duplicated atlas planning, strategy selection, and
CSS helpers. Imports pure functions from @layoutit/polycss and keeps only
React-specific JSX components, the useTextureAtlas hook, updateStableTriangleDom,
and solidTriangleStyle. Fixes the PROJECTIVE_QUAD_MAX_WEIGHT_RATIO divergence
(4 in fork vs Infinity in canonical) that caused visible rendering holes.
…olycss

Same migration as React: removes ~2000 lines of duplicated logic, imports
pure functions from @layoutit/polycss, retains Vue composable useTextureAtlas
(taking ComputedRef arguments and returning useFullRectSolid/useProjectiveQuad/
useStableTriangle/useBorderShape ComputedRefs), VNode render functions, and
updateStableTriangleDom. Fixes the same strategy selection divergence.
apresmoi added 29 commits May 22, 2026 03:19
Moves all 8 test files out of packages/polycss/src/render/__tests__/ and
places them as siblings of the source files they test. Pure-math tests
(plan, matrix, borderShape) move to packages/core/src/atlas/; polycss
wrapper/DOM tests (strategy, packing, paintDefaults, solidTrianglePrimitive,
stableTriangle) move to packages/polycss/src/render/atlas/. Imports updated
to reference local siblings or @layoutit/polycss-core where appropriate.
…atlas/

Mechanical file split only — zero behavior change, zero renames.

New files under packages/react/src/scene/atlas/:
  detection.ts       isBorderShapeSupported, isSolidTriangleSupported, getSolidPaintDefaultsFromPlans + doc-level helpers
  filterPlans.ts     filterAtlasPlans wrapper
  packing.ts         isMobileDocument, packTextureAtlasPlansWithScale
  buildAtlasPages.ts canvas pipeline (~340 lines, all internal helpers kept together)
  solidTriangleStyle.ts solidTriangleStyle math + shared geometry/color helpers
  stableTriangleDom.ts  updateStableTriangleDom — imports shared helpers from solidTriangleStyle.ts
  paintDefaults.ts   computeTextureAtlasPlan, getSolidPaintDefaults
  useTextureAtlas.ts React hook + TextureAtlasResult type
  triangle.tsx       TextureTrianglePoly component
  borderShape.tsx    TextureBorderShapePoly component + orderBrushInlineStyle helper
  projectiveSolid.tsx TextureProjectiveSolidPoly component
  atlasPoly.tsx      TextureAtlasPoly component
  index.tsx          barrel re-exporting all public symbols

Symbols added export (previously private, needed by stableTriangleDom.ts):
  offsetConvexPolygonPoints, formatStableTriangleTransformScalars,
  BASIS_EPS, SOLID_TRIANGLE_BLEED, DEFAULT_TILE, DEFAULT_LIGHT_DIR,
  DEFAULT_LIGHT_COLOR, DEFAULT_LIGHT_INTENSITY, DEFAULT_AMBIENT_COLOR,
  DEFAULT_AMBIENT_INTENSITY, RGB (interface), parseHex, rgbKey,
  rgbToHex, shadePolygon, quantizeCssColor, stepRgbToward

textureAtlas.tsx and atlasBrowser.ts reduced to thin re-export barrels.
Tests moved from scene/__tests__/ to atlas/ next to their sources.
Mirrors the React split. Mechanical file split only — zero behavior change, zero renames.

New files under packages/vue/src/scene/atlas/:
  detection.ts       isBorderShapeSupported, isSolidTriangleSupported, getSolidPaintDefaultsFromPlans
  filterPlans.ts     filterAtlasPlans wrapper
  packing.ts         isMobileDocument, packTextureAtlasPlansWithScale
  buildAtlasPages.ts canvas pipeline (identical to React copy)
  solidTriangleStyle.ts solidTriangleStyle math + shared geometry/color helpers
  stableTriangleDom.ts  updateStableTriangleDom — imports shared helpers from solidTriangleStyle.ts
  paintDefaults.ts   computeTextureAtlasPlan, getSolidPaintDefaults
  useTextureAtlas.ts Vue composable + TextureAtlasResult type
  triangle.ts        renderTextureTrianglePoly render function
  borderShape.ts     renderTextureBorderShapePoly + orderBrushInlineStyle helper
  projectiveSolid.ts renderTextureProjectiveSolidPoly render function
  atlasPoly.ts       renderTextureAtlasPoly render function
  index.ts           barrel re-exporting all public symbols

Symbols added export (previously private, needed by stableTriangleDom.ts):
  offsetConvexPolygonPoints, BASIS_EPS, SOLID_TRIANGLE_BLEED, DEFAULT_TILE,
  DEFAULT_LIGHT_DIR, DEFAULT_LIGHT_COLOR, DEFAULT_LIGHT_INTENSITY,
  DEFAULT_AMBIENT_COLOR, DEFAULT_AMBIENT_INTENSITY, RGB (interface),
  parseHex, rgbKey, rgbToHex, shadePolygon, quantizeCssColor, stepRgbToward

textureAtlas.ts and atlasBrowser.ts reduced to thin re-export barrels.
Tests moved from scene/__tests__/ to atlas/ next to their sources.
…Effect/onUpdated imperative write to prevent Concurrent Mode flicker on solid-triangle meshes
…er diagnosis

Adds __polycssDiag window object counting PolyScene/PolyMesh/TrianglePoly
renders, applyTransformDirect calls, and animate rAF ticks per second.
Logs to console every 1s. Remove after diagnosis.
…nt compositing flicker

The React and Vue stylesheets were missing `will-change: transform` on
.polycss-scene. Without it, the browser re-rasterizes every descendant leaf
on each animation frame instead of compositing a cached GPU layer, causing
visible flicker on solid-shape meshes (triangles, quads) that have no
opacity:0 loading phase to mask the re-paint. The vanilla polycss stylesheet
already carried this declaration with the same explanation.

Adds bench/flicker-diagnose.mjs: a Playwright-based diagnostic that observes
DOM mutations during orbit animation and reports the scene element's CSS
properties including will-change status.
@apresmoi apresmoi merged commit 19e1fc8 into main May 22, 2026
1 check passed
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