import type { Transform, Viewport } from '../types/cad.types'; export class ZoomPanController { private scale = 1; private offsetX = 0; private offsetY = 0; private canvas: HTMLCanvasElement; private isPanning = false; private lastX = 0; private lastY = 0; constructor(canvas: HTMLCanvasElement) { this.canvas = canvas; } getTransform(): Transform { return { a: this.scale, b: 0, c: 0, d: this.scale, e: this.offsetX, f: this.offsetY, }; } getViewport(): Viewport { const w = this.canvas.width / this.scale; const h = this.canvas.height / this.scale; const minX = -this.offsetX / this.scale || 0; const minY = -this.offsetY / this.scale || 0; return { minX, minY, maxX: minX + w, maxY: minY + h, }; } getScale(): number { return this.scale; } zoomAt(cx: number, cy: number, factor: number): void { const newScale = Math.max(0.01, Math.min(100, this.scale * factor)); const ratio = newScale / this.scale; this.offsetX = cx - (cx - this.offsetX) * ratio; this.offsetY = cy - (cy - this.offsetY) * ratio; this.scale = newScale; } zoomFit(elements: Array<{x:number;y:number;width:number;height:number}>): void { if (elements.length === 0) return; let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity; for (const el of elements) { minX = Math.min(minX, el.x - el.width/2); minY = Math.min(minY, el.y - el.height/2); maxX = Math.max(maxX, el.x + el.width/2); maxY = Math.max(maxY, el.y + el.height/2); } const padding = 40; const scaleX = (this.canvas.width - padding*2) / (maxX - minX); const scaleY = (this.canvas.height - padding*2) / (maxY - minY); this.scale = Math.min(scaleX, scaleY); this.offsetX = -minX * this.scale + padding; this.offsetY = -minY * this.scale + padding; } zoomToRect(rect: { minX: number; minY: number; maxX: number; maxY: number }): void { const padding = 20; const w = rect.maxX - rect.minX; const h = rect.maxY - rect.minY; if (w <= 0 || h <= 0) return; const scaleX = (this.canvas.width - padding * 2) / w; const scaleY = (this.canvas.height - padding * 2) / h; this.scale = Math.max(0.01, Math.min(100, Math.min(scaleX, scaleY))); this.offsetX = -rect.minX * this.scale + padding; this.offsetY = -rect.minY * this.scale + padding; } pan(dx: number, dy: number): void { this.offsetX += dx; this.offsetY += dy; } screenToWorld(sx: number, sy: number): { x: number; y: number } { const rect = this.canvas.getBoundingClientRect(); const x = (sx - rect.left - this.offsetX) / this.scale; const y = (sy - rect.top - this.offsetY) / this.scale; return { x, y }; } worldToScreen(wx: number, wy: number): { x: number; y: number } { return { x: wx * this.scale + this.offsetX, y: wy * this.scale + this.offsetY, }; } reset(): void { this.scale = 1; this.offsetX = 0; this.offsetY = 0; } }