import { BaseTool } from './base-tool'; import type { PointerState } from '@/types'; import { useLayerStore } from '@/store/layer-store'; import { useShapeStore } from '@/store/shape-store'; import { useHistoryStore } from '@/store/history-store'; import { DrawCommand } from '@/core/commands/draw-command'; import { drawRectangle, drawEllipse, drawLine, drawArrow, drawPolygon, drawStar, drawTriangle, } from '@/lib/shape-utils'; export class ShapeTool extends BaseTool { private startX = 0; private startY = 0; private currentX = 0; private currentY = 0; private drawCommand: DrawCommand | null = null; constructor() { super('Shape'); } onPointerDown(pointer: PointerState): void { this.isActive = true; this.isDrawing = true; this.startX = pointer.x; this.startY = pointer.y; this.currentX = pointer.x; this.currentY = pointer.y; const layer = this.getActiveLayer(); if (!layer) return; // Create draw command for history this.drawCommand = new DrawCommand(layer.id, 'Draw Shape'); } onPointerMove(pointer: PointerState, ctx: CanvasRenderingContext2D): void { if (!this.isDrawing) return; this.currentX = pointer.x; this.currentY = pointer.y; const layer = this.getActiveLayer(); if (!layer?.canvas) return; const layerCtx = layer.canvas.getContext('2d'); if (!layerCtx) return; // Clear and redraw from saved state if (this.drawCommand) { const beforeCanvas = (this.drawCommand as any).beforeCanvas; if (beforeCanvas) { layerCtx.clearRect(0, 0, layer.canvas.width, layer.canvas.height); layerCtx.drawImage(beforeCanvas, 0, 0); } } // Draw preview shape this.drawShape(layerCtx); } onPointerUp(): void { if (!this.isDrawing) return; const layer = this.getActiveLayer(); if (layer?.canvas) { const ctx = layer.canvas.getContext('2d'); if (ctx) { // Final draw this.drawShape(ctx); // Capture after state and add to history if (this.drawCommand) { this.drawCommand.captureAfterState(); const { executeCommand } = useHistoryStore.getState(); executeCommand(this.drawCommand); } } } this.isDrawing = false; this.isActive = false; this.drawCommand = null; } getCursor(): string { return 'crosshair'; } private drawShape(ctx: CanvasRenderingContext2D): void { const { settings } = useShapeStore.getState(); const x = Math.min(this.startX, this.currentX); const y = Math.min(this.startY, this.currentY); const width = Math.abs(this.currentX - this.startX); const height = Math.abs(this.currentY - this.startY); const cx = (this.startX + this.currentX) / 2; const cy = (this.startY + this.currentY) / 2; const radius = Math.sqrt(Math.pow(width / 2, 2) + Math.pow(height / 2, 2)); switch (settings.type) { case 'rectangle': drawRectangle(ctx, x, y, width, height, settings); break; case 'ellipse': drawEllipse(ctx, cx, cy, width / 2, height / 2, settings); break; case 'line': drawLine(ctx, this.startX, this.startY, this.currentX, this.currentY, settings); break; case 'arrow': drawArrow(ctx, this.startX, this.startY, this.currentX, this.currentY, settings); break; case 'polygon': drawPolygon(ctx, cx, cy, radius, settings.sides, settings); break; case 'star': drawStar(ctx, cx, cy, radius, settings.sides, settings.innerRadius, settings); break; case 'triangle': drawTriangle(ctx, x, y, width, height, settings); break; } } private getActiveLayer() { const { activeLayerId, layers } = useLayerStore.getState(); return layers.find((l) => l.id === activeLayerId); } }