201 lines
5.4 KiB
TypeScript
201 lines
5.4 KiB
TypeScript
|
|
import { BaseTool } from './base-tool';
|
||
|
|
import type { PointerState, TransformHandle, TransformBounds } from '@/types';
|
||
|
|
import { useLayerStore } from '@/store/layer-store';
|
||
|
|
import { useTransformStore } from '@/store/transform-store';
|
||
|
|
import {
|
||
|
|
getHandlePosition,
|
||
|
|
isNearHandle,
|
||
|
|
getHandleCursor,
|
||
|
|
calculateScaleFromHandle,
|
||
|
|
calculateRotationFromHandle,
|
||
|
|
} from '@/lib/transform-utils';
|
||
|
|
|
||
|
|
const HANDLES: TransformHandle[] = [
|
||
|
|
'top-left',
|
||
|
|
'top-center',
|
||
|
|
'top-right',
|
||
|
|
'middle-left',
|
||
|
|
'middle-right',
|
||
|
|
'bottom-left',
|
||
|
|
'bottom-center',
|
||
|
|
'bottom-right',
|
||
|
|
'rotate',
|
||
|
|
];
|
||
|
|
|
||
|
|
export class FreeTransformTool extends BaseTool {
|
||
|
|
private activeHandle: TransformHandle | null = null;
|
||
|
|
private dragStartX = 0;
|
||
|
|
private dragStartY = 0;
|
||
|
|
private originalState: any = null;
|
||
|
|
|
||
|
|
constructor() {
|
||
|
|
super('Free Transform');
|
||
|
|
}
|
||
|
|
|
||
|
|
onPointerDown(pointer: PointerState): void {
|
||
|
|
const { activeTransform } = useTransformStore.getState();
|
||
|
|
if (!activeTransform) {
|
||
|
|
// Start new transform
|
||
|
|
this.startTransform(pointer);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check if clicking on a handle
|
||
|
|
const handle = this.getHandleAtPoint(
|
||
|
|
pointer.x,
|
||
|
|
pointer.y,
|
||
|
|
activeTransform.originalBounds,
|
||
|
|
activeTransform.currentState
|
||
|
|
);
|
||
|
|
|
||
|
|
if (handle) {
|
||
|
|
this.isActive = true;
|
||
|
|
this.isDrawing = true;
|
||
|
|
this.activeHandle = handle;
|
||
|
|
this.dragStartX = pointer.x;
|
||
|
|
this.dragStartY = pointer.y;
|
||
|
|
this.originalState = { ...activeTransform.currentState };
|
||
|
|
} else {
|
||
|
|
// Check if clicking inside bounds (move)
|
||
|
|
const bounds = activeTransform.originalBounds;
|
||
|
|
const state = activeTransform.currentState;
|
||
|
|
|
||
|
|
if (
|
||
|
|
pointer.x >= bounds.x + state.x &&
|
||
|
|
pointer.x <= bounds.x + state.x + bounds.width * state.scaleX &&
|
||
|
|
pointer.y >= bounds.y + state.y &&
|
||
|
|
pointer.y <= bounds.y + state.y + bounds.height * state.scaleY
|
||
|
|
) {
|
||
|
|
this.isActive = true;
|
||
|
|
this.isDrawing = true;
|
||
|
|
this.activeHandle = null;
|
||
|
|
this.dragStartX = pointer.x;
|
||
|
|
this.dragStartY = pointer.y;
|
||
|
|
this.originalState = { ...activeTransform.currentState };
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
onPointerMove(pointer: PointerState): void {
|
||
|
|
if (!this.isDrawing) {
|
||
|
|
// Update cursor based on hover
|
||
|
|
this.updateCursor(pointer);
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
const { activeTransform, updateTransform, maintainAspectRatio } =
|
||
|
|
useTransformStore.getState();
|
||
|
|
if (!activeTransform || !this.originalState) return;
|
||
|
|
|
||
|
|
const dx = pointer.x - this.dragStartX;
|
||
|
|
const dy = pointer.y - this.dragStartY;
|
||
|
|
|
||
|
|
if (this.activeHandle === 'rotate') {
|
||
|
|
// Rotation
|
||
|
|
const bounds = activeTransform.originalBounds;
|
||
|
|
const centerX = bounds.x + bounds.width / 2 + activeTransform.currentState.x;
|
||
|
|
const centerY = bounds.y + bounds.height / 2 + activeTransform.currentState.y;
|
||
|
|
const rotation = calculateRotationFromHandle(centerX, centerY, pointer.x, pointer.y);
|
||
|
|
updateTransform({ rotation });
|
||
|
|
} else if (this.activeHandle) {
|
||
|
|
// Scale
|
||
|
|
const scale = calculateScaleFromHandle(
|
||
|
|
this.activeHandle,
|
||
|
|
this.dragStartX,
|
||
|
|
this.dragStartY,
|
||
|
|
pointer.x,
|
||
|
|
pointer.y,
|
||
|
|
activeTransform.originalBounds,
|
||
|
|
maintainAspectRatio
|
||
|
|
);
|
||
|
|
updateTransform({
|
||
|
|
scaleX: this.originalState.scaleX * scale.scaleX,
|
||
|
|
scaleY: this.originalState.scaleY * scale.scaleY,
|
||
|
|
});
|
||
|
|
} else {
|
||
|
|
// Move
|
||
|
|
updateTransform({
|
||
|
|
x: this.originalState.x + dx,
|
||
|
|
y: this.originalState.y + dy,
|
||
|
|
});
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
onPointerUp(): void {
|
||
|
|
this.isDrawing = false;
|
||
|
|
this.isActive = false;
|
||
|
|
this.activeHandle = null;
|
||
|
|
this.originalState = null;
|
||
|
|
}
|
||
|
|
|
||
|
|
getCursor(settings: any, pointer?: PointerState): string {
|
||
|
|
const { activeTransform } = useTransformStore.getState();
|
||
|
|
if (!activeTransform || !pointer) return 'default';
|
||
|
|
|
||
|
|
const handle = this.getHandleAtPoint(
|
||
|
|
pointer.x,
|
||
|
|
pointer.y,
|
||
|
|
activeTransform.originalBounds,
|
||
|
|
activeTransform.currentState
|
||
|
|
);
|
||
|
|
|
||
|
|
if (handle) {
|
||
|
|
return getHandleCursor(handle, activeTransform.currentState.rotation);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Check if inside bounds
|
||
|
|
const bounds = activeTransform.originalBounds;
|
||
|
|
const state = activeTransform.currentState;
|
||
|
|
|
||
|
|
if (
|
||
|
|
pointer.x >= bounds.x + state.x &&
|
||
|
|
pointer.x <= bounds.x + state.x + bounds.width * state.scaleX &&
|
||
|
|
pointer.y >= bounds.y + state.y &&
|
||
|
|
pointer.y <= bounds.y + state.y + bounds.height * state.scaleY
|
||
|
|
) {
|
||
|
|
return 'move';
|
||
|
|
}
|
||
|
|
|
||
|
|
return 'default';
|
||
|
|
}
|
||
|
|
|
||
|
|
private startTransform(pointer: PointerState): void {
|
||
|
|
const layer = this.getActiveLayer();
|
||
|
|
if (!layer?.canvas) return;
|
||
|
|
|
||
|
|
const bounds: TransformBounds = {
|
||
|
|
x: layer.x,
|
||
|
|
y: layer.y,
|
||
|
|
width: layer.canvas.width,
|
||
|
|
height: layer.canvas.height,
|
||
|
|
};
|
||
|
|
|
||
|
|
const { startTransform } = useTransformStore.getState();
|
||
|
|
startTransform(layer.id, bounds);
|
||
|
|
}
|
||
|
|
|
||
|
|
private getHandleAtPoint(
|
||
|
|
x: number,
|
||
|
|
y: number,
|
||
|
|
bounds: TransformBounds,
|
||
|
|
state: any
|
||
|
|
): TransformHandle | null {
|
||
|
|
for (const handle of HANDLES) {
|
||
|
|
if (isNearHandle(x, y, handle, bounds, state, 10)) {
|
||
|
|
return handle;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
|
||
|
|
private updateCursor(pointer: PointerState): void {
|
||
|
|
// This would update the canvas cursor dynamically
|
||
|
|
// Implementation depends on canvas component integration
|
||
|
|
}
|
||
|
|
|
||
|
|
private getActiveLayer() {
|
||
|
|
const { activeLayerId, layers } = useLayerStore.getState();
|
||
|
|
return layers.find((l) => l.id === activeLayerId);
|
||
|
|
}
|
||
|
|
}
|