// The view onto the surface: zoom steps and limits, zooming around a point, // fitting the content in. import { describe, expect, it } from 'vitest'; import { clampZoom, fitted, MAX_ZOOM, middle, MIN_ZOOM, shifted, spread, toScreen, toSurface, zoomedAt, zoomIn, zoomOut, ZOOM_STEPS } from '../src/canvas/viewport.js'; describe('the view onto the surface', () => { it('keeps the zoom between a quarter and twice', () => { expect([clampZoom(0.1), clampZoom(1.3), clampZoom(9)]).toEqual([MIN_ZOOM, 1.3, MAX_ZOOM]); }); it('steps along the ladder from any value, and back to where it was', () => { expect(ZOOM_STEPS.map(zoomIn)).toEqual([0.5, 0.75, 1, 1.25, 1.5, 2, 2]); expect(ZOOM_STEPS.map(zoomOut)).toEqual([0.25, 0.25, 0.5, 0.75, 1, 1.25, 1.5]); expect([zoomIn(0.8), zoomOut(0.8), zoomIn(1.0005)]).toEqual([1, 0.75, 1.25]); for (const step of ZOOM_STEPS.slice(1, -1)) expect(zoomOut(zoomIn(step))).toBe(step); }); it('turns screen and surface pixels into each other', () => { const view = { scale: 0.5, x: 100, y: -20 }; expect(toScreen(view, { x: 40, y: 80 })).toEqual({ x: 120, y: 20 }); expect(toSurface(view, toScreen(view, { x: 40, y: 80 }))).toEqual({ x: 40, y: 80 }); }); it('zooms so that the point zoomed at stays put, within the limits', () => { const view = { scale: 1, x: 30, y: 40 }; const point = { x: 200, y: 150 }; const zoomed = zoomedAt(view, 1.5, point); expect(zoomed.scale).toBe(1.5); expect(toScreen(zoomed, toSurface(view, point))).toEqual(point); expect(zoomedAt(view, 10, point).scale).toBe(MAX_ZOOM); expect(toScreen(zoomedAt(view, 10, point), toSurface(view, point))).toEqual(point); }); it('moves by what the pointer moved', () => { expect(shifted({ scale: 2, x: 1, y: 2 }, { x: 10, y: -5 })).toEqual({ scale: 2, x: 11, y: -3 }); }); it('fits the content in the middle with a margin, never above 100 %', () => { const content = { x: 100, y: 50, width: 800, height: 400 }; const view = fitted(content, { width: 448, height: 448 }); expect(view.scale).toBe(0.5); // Left and right margin alike, the content in the middle vertically. expect(toScreen(view, { x: 100, y: 50 })).toEqual({ x: 24, y: 124 }); expect(toScreen(view, { x: 900, y: 450 })).toEqual({ x: 424, y: 324 }); expect(fitted({ x: 0, y: 0, width: 100, height: 100 }, { width: 1000, height: 1000 }).scale).toBe(1); expect(fitted({ x: 0, y: 0, width: 100_000, height: 10 }, { width: 500, height: 500 }).scale).toBe(MIN_ZOOM); }); it('shows an empty surface at 100 % with its origin at the margin', () => { expect(fitted({ x: 0, y: 0, width: 0, height: 0 }, { width: 500, height: 500 })).toEqual({ scale: 1, x: 24, y: 24 }); expect(fitted({ x: 10, y: 10, width: 50, height: 50 }, { width: 0, height: 0 })).toEqual({ scale: 1, x: 14, y: 14 }); }); it('measures two fingers', () => { expect(spread({ x: 0, y: 0 }, { x: 3, y: 4 })).toBe(5); expect(middle({ x: 0, y: 10 }, { x: 4, y: 20 })).toEqual({ x: 2, y: 15 }); }); });