Skip to content

Performance: pen-tool pointermove re-render storm, per-piece decode of the same sheet image, tutorial 60fps full-panel re-renders, 1.25 MB unsplit bundle #100

Description

@dompm

Measured/traced hot spots, in order of payoff:

1. Pen tool: triple full-geometry pass per pointermove

Every pointermove in pen mode sets 4-5 pieces of state → full panel re-render (ResultPanel.tsx:1230-1237:703-842). Per event, findPenSnapTarget (:342), findAlignmentGuides (:491) and findLengthSnap (:599) each call flattenCurves over every piece — three dense-polygon allocations per move. The render then re-runs flattenCurves per piece again (:1765) and re-renders every PieceOverlay (not memoized; fresh displayPolygon array and onSelect closure each render, :1766-1777), so Konva reconciles every node and redraws the full-res pattern layer. SAM projects with dozens of pieces × hundreds of vertices make pen hover visibly laggy.

Fix: memoize flattened polygons per piece (keyed on polygon/curvePoints), React.memo PieceOverlay, build the snap-candidate set once per render instead of per move, move the hover preview to its own Konva layer.

2. One <img> + decode per piece for the same sheet texture

PieceOverlay calls useImage(glassImageUrl) per instance (ResultPanel.tsx:73); use-image has no cache (verified — fresh document.createElement('img') + decode per hook). Sheets are multi-MB base64 data URLs. 40 pieces on one sheet → 40 image elements and 40 async decodes on every remount. Lift the image per sheet (or add a module-level Map<url, HTMLImageElement>).

3. Tutorial animations re-render the world

  • Marching-ants: setDashOffset in a rAF loop re-renders all of ResultPanel at 60fps to advance a dash on one Rect (ResultPanel.tsx:996-1007). Use Konva.Animation/direct node mutation or isolate the rect in a memoized child.
  • SpotlightPulse.tsx:27-51 polls getBoundingClientRect in rAF (forced layout per frame). ResizeObserver would do.

4. getTooltipAnchor is O(total vertices) per pan/zoom frame

ResultPanel.tsx:199-241 (called at :2210): spreads min/max over the selected piece's polygon and computes centroids of all pieces every frame while panning. Also Math.min(...xs) on dense SAM polygons risks the V8 ~65k argument limit — use a loop. Memoize. (This whole function is deleted anyway if #34's docked inspector lands.)

5. Bundle: 1.25 MB main chunk, no code-splitting

vite build reports index-*.js at 1,247 kB (355 kB gzip) — three.js, Konva, ORT backend glue and the tutorial all load up front. three + Lamp3DPreview/LampProfileDialog are an obvious import() split (lamp mode is optional); the tutorial is another. Also consider manualChunks for konva/react vendors.

6. Structural: base64 images inside React state and undo snapshots

Pattern/sheet images live as data-URL strings in project state (ResultPanel.tsx:863-878, readAsDataURL), so every undo snapshot and every debounced autosave JSON.stringify re-serializes multi-MB strings on the main thread — every 500 ms during a drag. See the persistence-architecture refactor issue for the fix (OPFS-backed blobs + IDs).

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions