Renderer layer: satinRenderer.ts + fillRenderer.ts consume the optional gradient field (#350 renderer layer) #394
Labels
No labels
blocked:commit-ready-failed
blocked:gate-fetch
blocked:gate-lint
blocked:gate-plan
blocked:gate-research
blocked:needs-attention
blocked:needs-decision
blocked:needs-human
blocked:no-changes
blocked:verify-failed
bug
deferred
feature-request
in-progress
needs-decompose
needs-triage
quickfix
security/medium
triage-approved
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
Mark/Kimi_Agent_Brother_10_Digitizing_App#394
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Parent-Issue: #350
Depends on #393 (type layer —
StitchPath.gradient?: { startColor, endColor }). Do this issue only after #393 lands.Scope (renderer layer only — no UI changes)
Make
src/lib/canvas/satinRenderer.tsandsrc/lib/canvas/fillRenderer.tsconsume the new optionalgradientfield on the service stitch object. Whengradientis present, the renderer draws with a CanvasGradient fromstartColortoendColoralong the path's bounding-box axis (or along the satin direction for satin columns). Whengradientis absent, behavior is unchanged — solidthreadColor.Concrete change — sat
Both files currently have a line near the top of the render function:
(satinRenderer.ts:56, fillRenderer.ts:62). Replace with:
Then in the draw loop, build a
createLinearGradient(x0, y0, x1, y1)once per render, add both color stops, and use the gradient as the fill/stroke whenstartColor !== endColor. WhenstartColor === endColor(or both fall back tosolid), usesoliddirectly to skip the gradient machinery (no perf regression on the common no-gradient path).For satin (satinRenderer.ts:117):
becomes:
For fill (fillRenderer.ts:62): same shape, but
fillStyleinstead ofstroke.Gradient axis decision: along the path's bounding-box diagonal (top-left → bottom-right is the simplest visually-correct default; matches the hatomUI convention in the same canvas). If the renderer already has a bbox or transform context handy, use that — don't introduce a new bbox calc per render.
Files to touch
src/lib/canvas/satinRenderer.ts— gradient branch + stop arraysrc/lib/canvas/fillRenderer.ts— samesrc/lib/canvas/satinRenderer.test.ts— new test: a path withgradient: { startColor: '#ff0000', endColor: '#0000ff' }renders without throwing and produces a non-empty draw log; a path without gradient still usesthreadColor(regression guard)src/lib/canvas/fillRenderer.test.ts— sameAcceptance
pnpm testpasses including the new gradient test cases.Reference patterns
Look at how the renderer tests already build a
ServiceStitchObjectwiththreadColor: "#..."(satinRenderer.test.ts:13, fillRenderer.test.ts:13) — copy that constructor shape and add the gradient-bearing variant alongside.Discovery check
Confirm before starting:
should show the existing
const color = obj.threadColor ?? "#000000"lines exactly as cited above. (Verified 2026-10-05.)Landed as commit
90900a9on origin/main. Renderer-layer gradient consumption matches the type-layer shape fromab81a69. satinStitchObjectToFabricPath now readsobj.gradientand strokes with a Fabric.js Gradient along the path bbox diagonal when both stops are set and distinct; falls back to solidthreadColorotherwise. drawFillStitches likewise reads gradient on service StitchObject (workspace StitchObject does not yet carry gradient — that wires in with the UI layer). 16 new tests across both files (satin: stitchBoundingBox/gradientStops/buildSatinLinearGradient/gradient-path, fill: gradient describe with bbox-diagonal verify). tsc/eslint/37/37 unit tests/build all clean. Full vitest suite has 20 pre-existing failures (smart guides geometry) that predate this commit.