import { BaseTool } from './base-tool'; import type { PointerState, LassoPoint } from '@/types'; import { useSelectionStore } from '@/store/selection-store'; import { useLayerStore } from '@/store/layer-store'; import { createLassoMask, createSelection, combineMasks, } from '@/lib/selection-utils'; export class LassoSelectionTool extends BaseTool { private points: LassoPoint[] = []; private minDistance = 2; // Minimum distance between points constructor() { super('Lasso Selection'); } onPointerDown(pointer: PointerState): void { this.isActive = true; this.isDrawing = true; this.points = []; this.points.push({ x: pointer.x, y: pointer.y }); } onPointerMove(pointer: PointerState, ctx: CanvasRenderingContext2D): void { if (!this.isDrawing) return; const lastPoint = this.points[this.points.length - 1]; const dx = pointer.x - lastPoint.x; const dy = pointer.y - lastPoint.y; const distance = Math.sqrt(dx * dx + dy * dy); // Only add point if far enough from last point if (distance >= this.minDistance) { this.points.push({ x: pointer.x, y: pointer.y }); } // Draw preview const layer = this.getActiveLayer(); if (!layer?.canvas) return; ctx.clearRect(0, 0, layer.canvas.width, layer.canvas.height); ctx.save(); ctx.strokeStyle = '#000'; ctx.lineWidth = 1; ctx.setLineDash([4, 4]); ctx.beginPath(); ctx.moveTo(this.points[0].x, this.points[0].y); for (let i = 1; i < this.points.length; i++) { ctx.lineTo(this.points[i].x, this.points[i].y); } ctx.stroke(); ctx.strokeStyle = '#fff'; ctx.lineDashOffset = 4; ctx.stroke(); ctx.restore(); } onPointerUp(): void { if (!this.isDrawing) return; const layer = this.getActiveLayer(); if (!layer?.canvas || this.points.length < 3) { this.isDrawing = false; this.isActive = false; return; } // Close the path if not already closed const firstPoint = this.points[0]; const lastPoint = this.points[this.points.length - 1]; const dx = lastPoint.x - firstPoint.x; const dy = lastPoint.y - firstPoint.y; const distance = Math.sqrt(dx * dx + dy * dy); if (distance > this.minDistance) { this.points.push(firstPoint); } const { selectionMode, feather, activeSelection } = useSelectionStore.getState(); const newMask = createLassoMask( this.points, layer.canvas.width, layer.canvas.height ); let finalMask = newMask; // Combine with existing selection if needed if (activeSelection && selectionMode !== 'new') { finalMask = combineMasks(activeSelection.mask, newMask, selectionMode); } const selection = createSelection(layer.id, finalMask, feather); useSelectionStore.getState().setActiveSelection(selection); this.isDrawing = false; this.isActive = false; this.points = []; } getCursor(): string { return 'crosshair'; } private getActiveLayer() { const { activeLayerId, layers } = useLayerStore.getState(); return layers.find((l) => l.id === activeLayerId); } }