/** * The view rules of the pixel art tab (#130) that need no Pixi: whole zoom steps, where the grid * starts, and which pixel a pointer is over – so they can be checked without a browser. */ import type { SheetView } from '../texture/sheetView'; /** The zoom steps (answer 9: whole steps from 1x to 32x). Doubling alone jumps too far in the middle. */ const ZOOM_STEPS = [1, 2, 3, 4, 6, 8, 12, 16, 24, 32] as const; /** From this zoom on the pixel grid is drawn; below it the lines would be denser than the pixels. */ export const GRID_FROM_ZOOM = 8; /** The largest step at which the canvas still fits the area, never below 1x. */ export function fitZoom(canvasW: number, canvasH: number, areaW: number, areaH: number): number { let zoom: number = ZOOM_STEPS[0]; for (const step of ZOOM_STEPS) if (canvasW * step <= areaW * 0.9 && canvasH * step <= areaH * 0.9) zoom = step; return zoom; } /** The next step up (`direction` 1) or down (-1) from `zoom`; stays at the ends. */ export function stepZoom(zoom: number, direction: 1 | -1): number { if (direction > 0) return ZOOM_STEPS.find((step) => step > zoom) ?? ZOOM_STEPS[ZOOM_STEPS.length - 1]!; return [...ZOOM_STEPS].reverse().find((step) => step < zoom) ?? ZOOM_STEPS[0]; } /** The canvas centred in the area at a zoom, on whole screen pixels so the pixel edges stay sharp. */ export function centredView( canvasW: number, canvasH: number, areaW: number, areaH: number, zoom: number, ): SheetView { return { panX: Math.round((areaW - canvasW * zoom) / 2), panY: Math.round((areaH - canvasH * zoom) / 2), zoom, }; } /** The pixel under a widget-local point – anywhere, inside the canvas or not. */ export function pixelUnder(view: SheetView, localX: number, localY: number): { x: number; y: number } { return { x: Math.floor((localX - view.panX) / view.zoom), y: Math.floor((localY - view.panY) / view.zoom) }; }