Files
paint-ui/tools/shape-tool.ts

144 lines
3.8 KiB
TypeScript
Raw Normal View History

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);
}
}