refactor(editor): size drawing through the tracked canvas transform - #503
Merged
Merged
Conversation
kostyafarber
force-pushed
the
refactor/canvas-transform-cleanup
branch
from
October 3, 2026 14:49
6ce5b85 to
c0bb6cb
Compare
Pixel-sized strokes and markers divided by camera zoom, which is only right while every node draws at unit scale. Canvas now tracks the transform from its current drawing units to screen pixels, so pxToUnits, visible-bounds culling and the marker matrix follow whatever node transform is active, and the selection bounding box draws its chrome directly in screen space.
Widths, dashes and radii are screen pixels, so Canvas now applies them in screen space: strokes reset to pixels after the path is built, Path2D strokes are mapped to the screen, and handles, snap crosses and the lock icon project their anchors and draw in pixels. Nothing converts pixels to drawing units any more, so pxToUnits is gone.
… copies Mapping each outline Path2D into screen space copied the whole path on every stroke, and the CPU handle fallback built an array of projected handles each frame. Retained paths now stroke in place with the pixel width scaled into the current units, and handles project inside the draw loop.
Anchors called the screen-space handle drawing while the canvas was still in glyph units, so their size scaled with zoom. Project anchors and draw them in screen space like the other handles.
Screen-space helpers took a plain Canvas and Point2D, so calling them from node units compiled and drew at the wrong size, as anchors did. withScreenSpace now hands its callback a branded ScreenCanvas and a projection from the outer units, and those helpers require both.
kostyafarber
force-pushed
the
refactor/canvas-transform-cleanup
branch
from
October 3, 2026 15:42
c0bb6cb to
1acee6a
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Follows #502.
Summary
Canvastracks the transform from its current drawing units to logical screen pixels, throughwithTransform,save/restore,translate,scaleandrotate.pxToUpmis gone). Geometry is drawn in the current units; widths, dashes and radii are screen pixels applied in screen space.Canvas.stroke()strokes the current path after resetting to pixels.strokePathmaps thePath2Dto the screen withaddPath(path, transform).toScreenand draw in pixels.withScreenSpace((screen, project) => …)hands its callback a brandedScreenCanvasand a projection from the outer units, and the screen-only helpers (handle and anchor drawing, the bounding-box handles) require aScreenCanvasandScreenPoints. Calling them from node units no longer compiles. That mistake had made anchor diamonds scale with zoom.Canvas.visibleBounds). This removes theEditor.screenTransformandEditor.visibleLocalBoundsplumbing, plus the unusedEditor.screenToSceneDistance.Canvas.withScreenSpace), so it no longer offsets a scene rect by pixel distances converted at the current zoom.-rotationthat assumed a Y flip.Walking the node tree for drawing is left to the text-run work:
sceneTransformalready places child nodes correctly, and per-pass child dispatch belongs with the run's design.Issue
Refs #129
Refs #458
Testing
pnpm check(lint, typecheck, browser check, strict dead code, unit tests) passes, plus the pre-commit suite on each commit.pnpm format:checkpasses.glyph-renderingandtools.🤖 Generated with Claude Code