Files
image-studio/commands/viewport.ts
2026-07-03 11:31:02 +02:00

140 lines
3.1 KiB
TypeScript

import type { Vec2D } from "@core/geometry";
import type { Command } from "./command";
import { commandIds } from "./ids";
export type ViewportPanPayload = {
delta: Vec2D;
};
export type ViewportSetZoomPayload = {
zoom: number;
};
export type ViewportZoomAroundPointPayload = {
zoom: number;
point: Vec2D;
};
export type ViewportSetSizePayload = {
w: number;
h: number;
};
export const viewportPanCommand: Command<ViewportPanPayload> = {
id: commandIds.viewportPan,
name: "Pan viewport",
execute({ state }, payload) {
return {
...state,
editor: {
...state.editor,
viewport: {
...state.editor.viewport,
center: {
x: state.editor.viewport.center.x + payload.delta.x,
y: state.editor.viewport.center.y + payload.delta.y,
},
},
},
};
},
};
export const viewportSetZoomCommand: Command<ViewportSetZoomPayload> = {
id: commandIds.viewportSetZoom,
name: "Set viewport zoom",
execute({ state }, payload) {
const zoom = Math.max(0.01, payload.zoom);
return {
...state,
editor: {
...state.editor,
viewport: {
...state.editor.viewport,
zoom,
},
},
};
},
};
export const viewportZoomAroundPointCommand: Command<ViewportZoomAroundPointPayload> = {
id: commandIds.viewportZoomAroundPoint,
name: "Zoom viewport around point",
execute({ state }, payload) {
const viewport = state.editor.viewport;
const zoom = Math.max(0.01, payload.zoom);
const offset = {
x: payload.point.x - viewport.size.w / 2,
y: payload.point.y - viewport.size.h / 2,
};
const documentPoint = {
x: viewport.center.x + offset.x / viewport.zoom,
y: viewport.center.y + offset.y / viewport.zoom,
};
return {
...state,
editor: {
...state.editor,
viewport: {
...viewport,
zoom,
center: {
x: documentPoint.x - offset.x / zoom,
y: documentPoint.y - offset.y / zoom,
},
},
},
};
},
};
export const viewportSetSizeCommand: Command<ViewportSetSizePayload> = {
id: commandIds.viewportSetSize,
name: "Set viewport size",
execute({ state }, payload) {
return {
...state,
editor: {
...state.editor,
viewport: {
...state.editor.viewport,
size: {
w: Math.max(0, payload.w),
h: Math.max(0, payload.h),
},
},
},
};
},
};
export const viewportResetCommand: Command = {
id: commandIds.viewportReset,
name: "Reset viewport",
execute({ state }) {
return {
...state,
editor: {
...state.editor,
viewport: {
center: { x: 0, y: 0 },
zoom: 1,
rotation: 0,
size: state.editor.viewport.size,
},
},
};
},
};
export const viewportCommands = [
viewportPanCommand,
viewportSetZoomCommand,
viewportZoomAroundPointCommand,
viewportSetSizeCommand,
viewportResetCommand,
] satisfies Command<unknown>[];