feat: initial commit web-cad-neu with docker-compose, frontend and backend
This commit is contained in:
@@ -0,0 +1,97 @@
|
||||
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;
|
||||
return {
|
||||
minX: -this.offsetX / this.scale,
|
||||
minY: -this.offsetY / this.scale,
|
||||
maxX: (-this.offsetX / this.scale) + w,
|
||||
maxY: (-this.offsetY / this.scale) + 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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user