From 03d4d4a3b9654298adaa00096c66b7a75cb13b3d Mon Sep 17 00:00:00 2001 From: syntaxbullet Date: Fri, 3 Jul 2026 16:05:42 +0200 Subject: [PATCH] feat(renderer): draw transform handles --- renderer/renderer.ts | 2 ++ renderer/transform-controls.ts | 43 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) create mode 100644 renderer/transform-controls.ts diff --git a/renderer/renderer.ts b/renderer/renderer.ts index d26b82e..91c1494 100644 --- a/renderer/renderer.ts +++ b/renderer/renderer.ts @@ -2,6 +2,7 @@ import type { ImageDocument } from "@core/document"; import type { EditorState } from "@editor/state"; import { renderArtboard } from "./artboard"; import { renderSelectionOverlay } from "./selection"; +import { renderTransformControls } from "./transform-controls"; import type { WebGlRendererContext } from "./types"; export type RenderFrame = { @@ -46,6 +47,7 @@ export function createRenderer(canvas: HTMLCanvasElement, backend: RendererBacke } renderSelectionOverlay(rendererContext, frame.document, frame.editor); + renderTransformControls(rendererContext, frame.document, frame.editor); context.disable(context.SCISSOR_TEST); }, diff --git a/renderer/transform-controls.ts b/renderer/transform-controls.ts new file mode 100644 index 0000000..17833d4 --- /dev/null +++ b/renderer/transform-controls.ts @@ -0,0 +1,43 @@ +import type { ImageDocument } from "@core/document"; +import type { EditorState } from "@editor/state"; +import { clearScreenRect } from "./clear-rect"; +import { documentRectToScreenRect } from "./screen-rect"; +import type { RgbaColor, ScreenRect, WebGlRendererContext } from "./types"; + +const handleColor: RgbaColor = [1, 1, 1, 1]; +const handleBorderColor: RgbaColor = [0.1, 0.65, 1, 1]; + +export function renderTransformControls(context: WebGlRendererContext, document: ImageDocument, editor: EditorState) { + const selectedArtboard = editor.selection.artboardId + ? document.artboards.find((artboard) => artboard.id === editor.selection.artboardId) + : undefined; + + if (!selectedArtboard) return; + + const rect = documentRectToScreenRect(context.canvas, selectedArtboard.bounds, editor.viewport); + for (const handle of transformHandleRects(rect)) { + clearScreenRect(context, handle.border, handleBorderColor); + clearScreenRect(context, handle.fill, handleColor); + } +} + +function transformHandleRects(rect: ScreenRect) { + const size = 8; + const border = 2; + const half = size / 2; + const points = [ + { x: rect.x, y: rect.y }, + { x: rect.x + rect.w / 2, y: rect.y }, + { x: rect.x + rect.w, y: rect.y }, + { x: rect.x + rect.w, y: rect.y + rect.h / 2 }, + { x: rect.x + rect.w, y: rect.y + rect.h }, + { x: rect.x + rect.w / 2, y: rect.y + rect.h }, + { x: rect.x, y: rect.y + rect.h }, + { x: rect.x, y: rect.y + rect.h / 2 }, + ]; + + return points.map((point) => ({ + border: { x: Math.round(point.x - half - border), y: Math.round(point.y - half - border), w: size + border * 2, h: size + border * 2 }, + fill: { x: Math.round(point.x - half), y: Math.round(point.y - half), w: size, h: size }, + })); +}