Files
galaxy-game/ui/frontend/tests/map-display-sizing.test.ts
T
Ilia Denisov 680ebac919
Tests · UI / test (push) Waiting to run
Tests · UI / test (pull_request) Failing after 5m16s
feat(ui): F8-12 — map polish (zoom invariance, labels, selection, soft radius) (#55)
* Honest pixel-space sizing for `pointRadiusPx` / `strokeWidthPx`: the
  renderer divides by the current camera scale on every
  `viewport.zoomed` so thin lines / small markers stay the same on-screen
  size at any zoom.
* Known-size planets switch to `pointRadiusWorld`, softened against the
  reference scale by `PLANET_SIZE_ZOOM_ALPHA = 0.33`; unidentified
  planets pin to a 3-px disc.
* New planet label layer renders a two-line `name / #N` legend under
  each planet (`#N` only for unidentified or when the new `planetNames`
  toggle is off). Selection now paints an inverse-fill frame around the
  selected planet's label plus an outline on the disc; the old
  selection-ring primitive is retired.
* Bombing markers swap the separate CirclePrim for a planet-outline
  overlay (damaged / wiped colour); the report deep-link moves to a
  "view bombing report" link in the planet inspector.
* Docs + tests follow: `renderer.md` reflects the new sizing contract +
  label / outline layers, vitest covers the sizing math, label
  formatting, and the new toggle, and the map-toggles e2e adds a
  persistence case for `planetNames`.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 23:51:16 +02:00

84 lines
2.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Coverage for the F8-12 sizing helpers in src/map/world.ts:
// `displayPointRadiusWorld` (the union of the pixel-space and the
// softened-by-zoom rules) and `displayStrokeWidthWorld` (pixel-space
// stroke widths). Both are pure math, so this file stays Pixi-free.
import { describe, expect, test } from "vitest";
import {
DEFAULT_POINT_RADIUS_PX,
PLANET_SIZE_ZOOM_ALPHA,
displayPointRadiusWorld,
displayStrokeWidthWorld,
} from "../src/map/world";
describe("displayPointRadiusWorld — pixel-space (pointRadiusPx)", () => {
test("returns pixel size divided by scale at scale=1", () => {
expect(displayPointRadiusWorld({ pointRadiusPx: 5 }, 1, 0.2)).toBe(5);
});
test("shrinks the world footprint as zoom grows", () => {
expect(displayPointRadiusWorld({ pointRadiusPx: 6 }, 3, 0.2)).toBeCloseTo(2);
});
test("falls back to DEFAULT_POINT_RADIUS_PX when the style is bare", () => {
expect(displayPointRadiusWorld({}, 2, 0.2)).toBeCloseTo(
DEFAULT_POINT_RADIUS_PX / 2,
);
});
test("zero-scale guard returns the raw pixel size", () => {
expect(displayPointRadiusWorld({ pointRadiusPx: 4 }, 0, 0.2)).toBe(4);
});
});
describe("displayPointRadiusWorld — softened by zoom (pointRadiusWorld)", () => {
test("at scale=scaleRef the visible radius equals the base radius", () => {
const radius = displayPointRadiusWorld(
{ pointRadiusWorld: 6 },
0.2,
0.2,
);
expect(radius).toBeCloseTo(6);
});
test("zooming in grows the radius sub-linearly", () => {
const r1 = displayPointRadiusWorld({ pointRadiusWorld: 6 }, 0.2, 0.2);
const r10 = displayPointRadiusWorld({ pointRadiusWorld: 6 }, 2.0, 0.2);
// On-screen pixel size grows by scale^α (α = 0.33) instead of
// linearly: 10x zoom → ~10^0.33 ≈ 2.15x growth.
const onScreenAt1 = r1 * 0.2;
const onScreenAt10 = r10 * 2.0;
expect(onScreenAt10 / onScreenAt1).toBeCloseTo(
Math.pow(10, PLANET_SIZE_ZOOM_ALPHA),
3,
);
});
test("ignores pointRadiusPx when pointRadiusWorld is set", () => {
const r = displayPointRadiusWorld(
{ pointRadiusPx: 99, pointRadiusWorld: 4 },
0.4,
0.2,
);
// World radius is the base softened by (0.4/0.2)^(α-1).
expect(r).toBeCloseTo(4 * Math.pow(2, PLANET_SIZE_ZOOM_ALPHA - 1), 4);
});
});
describe("displayStrokeWidthWorld", () => {
test("returns width / scale at any zoom", () => {
expect(displayStrokeWidthWorld({ strokeWidthPx: 2 }, 1)).toBe(2);
expect(displayStrokeWidthWorld({ strokeWidthPx: 2 }, 4)).toBeCloseTo(0.5);
expect(displayStrokeWidthWorld({ strokeWidthPx: 2 }, 0.5)).toBeCloseTo(4);
});
test("falls back to 1 when strokeWidthPx is omitted", () => {
expect(displayStrokeWidthWorld({}, 2)).toBeCloseTo(0.5);
});
test("zero-scale guard returns the raw pixel value", () => {
expect(displayStrokeWidthWorld({ strokeWidthPx: 3 }, 0)).toBe(3);
});
});