/** * DrawingTool - Pencil/Brush/Eraser drawing on canvas layers * * Extracted from DrawToolCore.paintOnCanvas() closure (Phase 3). * Handles left-click drawing operations: pencil stroke + fill, brush strokes, * eraser, and undo snapshot capture/push. * * Phase B: Brush mode now writes voxels directly during mousemove and * re-renders via marching squares, eliminating the visual "snap" between * the smooth canvas stroke and the voxel-based post-release render. */ import { BaseTool } from "./BaseTool"; import type { ToolContext } from "./BaseTool"; import type { DrawingHostDeps } from "./ToolHost"; export declare class DrawingTool extends BaseTool { /** Left mouse button currently held (draw mode) */ private leftClicked; /** True while actively painting (between pointerdown and pointerup) */ private isPainting; /** Accumulated pencil stroke points for fill-on-release */ private drawingLines; /** Eraser arc function, assigned once per paintOnCanvas() call */ private clearArcFn; /** Snapshot of the active layer's slice captured on mouse-down (before drawing) */ private preDrawSlice; private preDrawAxis; private preDrawSliceIndex; /** Previous voxel-space position for Bresenham interpolation (brush mode) */ private prevVoxel; private callbacks; constructor(ctx: ToolContext, callbacks: DrawingHostDeps); /** Whether a draw operation is currently active */ get isActive(): boolean; /** Whether paint strokes are being recorded */ get painting(): boolean; /** * Reset drawing state. Called at start of each paintOnCanvas() cycle. * @param clearArcFn - Eraser function for this paint cycle */ reset(clearArcFn: (x: number, y: number, size: number) => void): void; /** * Called on left-click pointerdown in draw mode. * Sets up drawing state, cursor, undo snapshot, and start position. */ onPointerDown(e: MouseEvent): void; /** * Called on pointermove during an active drawing operation. * Handles eraser clearing or brush/pencil stroke accumulation. */ onPointerMove(e: MouseEvent): void; /** * Called on pointerup in draw mode. * Completes stroke, fills pencil path, syncs volume, pushes undo delta. */ onPointerUp(_e: MouseEvent): void; /** * Called on pointerleave during drawing. * Resets painting state and cleans up leftClicked. * @returns true if drawing was in progress when leave occurred */ onPointerLeave(): boolean; /** * Create a self-managing mouseover/mouseout/mousemove handler * that tracks brush hover position for the preview circle. */ createBrushTrackingHandler(): (e: MouseEvent) => void; /** * Render the size-preview ring for the brush / eraser on the drawing context. * * - Brush: solid outline in the current brush colour. * - Eraser: red dashed outline (matches the sphereEraser preview), so the * eraser no longer needs a sized PNG cursor — the ring tracks the live * `brushAndEraserSize` and is what the user resizes with Shift+wheel. * * Only the pencil has no preview ring (its stroke width is fixed). */ renderBrushPreview(ctx: CanvasRenderingContext2D, _width?: number, _height?: number): void; /** * Convert display (zoomed) coordinates to 3D voxel coordinates. * Same logic as SphereBrushTool.canvasToVoxelCenter. */ private canvasToVoxel3D; /** * Get voxel-space brush radius for visible axes. * Converts display-pixel brush size to voxel units. */ private getVoxelBrushRadius; /** * Paint a 2D ellipse of voxels on the current slice. * Only the two visible axes are iterated; the slice-direction axis is fixed. */ private paintVoxelEllipse; /** * Brush mode mousemove: write voxels along stroke segment, then re-render. * Uses linear interpolation between prev and current positions to ensure * no gaps when the mouse moves fast. */ private paintBrushVoxelMove; /** * Re-render the active layer's canvas from MaskVolume via marching squares, * then composite all layers to master. */ private refreshLayerFromVolume; /** * Notify the backend of the brush stroke by firing onMaskChanged for the * current slice. Brush writes voxels straight into the MaskVolume (no canvas * bake), so we read the authoritative slice back from the volume — mirrors * SphereBrushTool.refreshDisplay's notify path. Brush only ever touches the * current view slice (canvasToVoxel3D fixes the slice axis), so a single * slice notification is sufficient. */ private notifyMaskChanged; /** Capture pre-draw slice snapshot for undo */ private capturePreDrawSnapshot; /** Push delta to UndoManager after drawing completes */ private pushUndoDelta; /** * Redraws persisted layer data onto ctx before new pencil fill. * Delegates to the host's vector renderSliceToCanvas for consistency. */ private redrawPreviousImageToLayerCtx; /** Draw a line segment on a layer canvas (pencil mode only) */ private drawLinesOnLayer; /** Paint a segment on the current layer canvas and composite to master (pencil mode) */ private paintOnCanvasLayer; }