From 98b9b300d90526c523225a0c1e38c58b5d21e8c8 Mon Sep 17 00:00:00 2001 From: MelekTaus Date: Sun, 27 Sep 2026 12:12:01 +0200 Subject: [PATCH] Zoom the scene view further in, up to 64 device pixels The scene view stopped at 24 device pixels per world pixel, which is too little to work on single texels of small pixel art. - FREE_ZOOM_STEPS gains the steps 32, 48 and 64. All of them are whole numbers, so every texel keeps the same width on screen. - The tile editor takes the same steps, so its fit can now land above 24 on a small board. - The unit test and the step list in e2e 39 follow the new top step. Tests: - ./check.sh: 178 files, 2573 tests, CHECK EXIT 0 Co-Authored-By: Claude Opus 5.5 --- src/viewport/view.ts | 2 +- tests/viewport/view.test.ts | 6 ++++-- tools/e2e/39-tile.mjs | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/viewport/view.ts b/src/viewport/view.ts index 4a7f3ac9..7e5dc9fe 100644 --- a/src/viewport/view.ts +++ b/src/viewport/view.ts @@ -26,7 +26,7 @@ export function freeView(x: number, y: number, zoom: number, w: number, h: numbe * The zoom steps of the scene view, in device pixels per world pixel. Each gives every texel the * same number of screen pixels, so pixel art never shows one pixel wider than its neighbour. */ -export const FREE_ZOOM_STEPS = [0.125, 0.25, 0.5, 1, 2, 3, 4, 5, 6, 8, 12, 16, 24] as const; +export const FREE_ZOOM_STEPS = [0.125, 0.25, 0.5, 1, 2, 3, 4, 5, 6, 8, 12, 16, 24, 32, 48, 64] as const; /** The largest step at or below `zoom`, never below the smallest (fit rounds down to it). */ export function zoomStepBelow(zoom: number): number { diff --git a/tests/viewport/view.test.ts b/tests/viewport/view.test.ts index 74346316..8812908e 100644 --- a/tests/viewport/view.test.ts +++ b/tests/viewport/view.test.ts @@ -59,7 +59,8 @@ describe('free view zoom steps under Windows scaling', () => { expect(stepFreeZoom(2, -1)).toBe(1); expect(stepFreeZoom(1, -1)).toBe(0.5); expect(stepFreeZoom(6, 1)).toBe(8); - expect(stepFreeZoom(24, 1)).toBe(24); + expect(stepFreeZoom(24, 1)).toBe(32); + expect(stepFreeZoom(64, 1)).toBe(64); expect(stepFreeZoom(0.125, -1)).toBe(0.125); }); @@ -68,7 +69,8 @@ describe('free view zoom steps under Windows scaling', () => { expect(zoomStepBelow(0.7)).toBe(0.5); expect(zoomStepBelow(0.01)).toBe(0.125); expect(zoomStepBelow(20)).toBe(16); - expect(zoomStepBelow(100)).toBe(24); + expect(zoomStepBelow(40)).toBe(32); + expect(zoomStepBelow(100)).toBe(64); expect(nearestZoomStep(1.37)).toBe(1); expect(nearestZoomStep(0.4)).toBe(0.5); }); diff --git a/tools/e2e/39-tile.mjs b/tools/e2e/39-tile.mjs index 30999e64..4565aa21 100644 --- a/tools/e2e/39-tile.mjs +++ b/tools/e2e/39-tile.mjs @@ -211,7 +211,7 @@ export async function tile(t) { // Fit used to stop at 100 %, which left a board measured in 16 px tiles a stamp in the corner. // It takes the scene view's steps (device pixels, `FREE_ZOOM_STEPS`): the largest one the board // fits at, so the next one up would run past the margin. - const STEPS = [0.125, 0.25, 0.5, 1, 2, 3, 4, 5, 6, 8, 12, 16, 24]; + const STEPS = [0.125, 0.25, 0.5, 1, 2, 3, 4, 5, 6, 8, 12, 16, 24, 32, 48, 64]; const fills = (b) => { const at = STEPS.findIndex((step) => Math.abs(step - b.zoom * b.ratio) < 1e-9); const fits = (step) =>