Remove comments that only restate the code, keeping the section banners and the notes that carry a reason, a measured fact or an external contract. ButtonOptions.width claimed to be a fixed width but only set minWidth, so the Graph and Texture pickers grew with the name they showed. It is now what it promises, with a separate minWidth for the buttons that have two forms. Narrow buttons shrink their padding, otherwise a 24px icon button had 8px left for its glyph and truncate dropped it entirely. __gm.labelsDropped() guards that class: the overflow guard only saw text that was too wide, never a label truncated to nothing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
56 lines
2.0 KiB
TypeScript
56 lines
2.0 KiB
TypeScript
/** The viewport transforms: plain geometry, so pinned here rather than through the browser. */
|
||
import { describe, expect, it } from 'vitest';
|
||
import {
|
||
cameraView,
|
||
freeView,
|
||
isInside,
|
||
screenToWorld,
|
||
worldBounds,
|
||
worldToScreen,
|
||
} from '../../src/viewport/view';
|
||
|
||
describe('freeView', () => {
|
||
it('puts the view centre in the middle of the widget', () => {
|
||
const view = freeView(50, -20, 2, 800, 600);
|
||
expect(worldToScreen(view, 50, -20)).toEqual({ x: 400, y: 300 });
|
||
expect(screenToWorld(view, 400, 300)).toEqual({ x: 50, y: -20 });
|
||
});
|
||
|
||
it('scales distances by the zoom', () => {
|
||
const view = freeView(0, 0, 2, 800, 600);
|
||
expect(worldToScreen(view, 100, 0)).toEqual({ x: 600, y: 300 });
|
||
});
|
||
|
||
it('round-trips any point', () => {
|
||
const view = freeView(13, -7, 0.35, 640, 480);
|
||
const back = screenToWorld(view, ...(Object.values(worldToScreen(view, 123, -456)) as [number, number]));
|
||
expect(back.x).toBeCloseTo(123, 9);
|
||
expect(back.y).toBeCloseTo(-456, 9);
|
||
});
|
||
|
||
it('covers the widget in world units', () => {
|
||
const bounds = worldBounds(freeView(0, 0, 2, 800, 600));
|
||
expect(bounds).toEqual({ left: -200, top: -150, right: 200, bottom: 150 });
|
||
});
|
||
});
|
||
|
||
describe('cameraView', () => {
|
||
const camera = { x: 10, y: 20, props: { width: 640, height: 360 } };
|
||
|
||
it('fits the camera frame into the widget and centres it', () => {
|
||
// 640×360 into 800×600: width is the tighter fit (1.25 vs 1.67), so bars top and bottom.
|
||
const view = cameraView(camera, 800, 600);
|
||
expect(view.scale).toBeCloseTo(1.25, 9);
|
||
expect(view.rect).toEqual({ x: 0, y: 75, w: 800, h: 450 });
|
||
// The camera position sits at the centre of the frame, not of the widget.
|
||
expect(worldToScreen(view, 10, 20)).toEqual({ x: 400, y: 300 });
|
||
});
|
||
|
||
it('reports the letterbox as outside', () => {
|
||
const view = cameraView(camera, 800, 600);
|
||
expect(isInside(view, 400, 300)).toBe(true);
|
||
expect(isInside(view, 400, 20)).toBe(false); // top bar
|
||
expect(isInside(view, 400, 580)).toBe(false); // bottom bar
|
||
});
|
||
});
|