Files
galaxy-game/ui/frontend/tests/map-display-sizing.test.ts
T
Ilia Denisov 6996a79286
Tests · UI / test (push) Has been cancelled
Tests · UI / test (pull_request) Successful in 3m20s
perf(ui): F8-12 — pixel-space planet sizing + single-copy label/outline layers (#55)
* Planet size formula moves to pixel-space:
  `pointRadiusBasePx = 2 + 2 * cbrt(size / SIZE_NORMALIZER)`. The
  on-screen disc now reads ~4-7 px at the reference zoom regardless
  of how large the world rectangle is — the previous `world-units`
  formulation blew up on small maps and made Source-class planets
  swallow their neighbours.
* Labels + outlines live in the origin copy only. The 9× replication
  across torus copies was the dominant cost on a 100+ planet map
  (Pixi.Text creation + Graphics rebuilds on every zoom step); the
  origin-copy layout is what the camera-wrap listener guarantees
  the user actually sees.
* `setPlanetLabels` and `setPlanetOutlines` skip Pixi-object
  rebuilds when the input fingerprint is unchanged — toggle flips
  and selection changes now keep the existing Text / Graphics
  instances alive and only repaint the affected pieces.
* `renderer.md` updated to the new contract.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-28 00:39:19 +02:00

89 lines
3.0 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 (pointRadiusBasePx)", () => {
test("at scale=scaleRef the on-screen pixel size equals the base", () => {
const radius = displayPointRadiusWorld(
{ pointRadiusBasePx: 6 },
0.2,
0.2,
);
// world units → 6 (base px) / 0.2 (scale) = 30
expect(radius).toBeCloseTo(30);
// confirm pixel-space: world * scale ≈ 6.
expect(radius * 0.2).toBeCloseTo(6);
});
test("zooming in grows the on-screen pixel size sub-linearly", () => {
const r1 = displayPointRadiusWorld({ pointRadiusBasePx: 6 }, 0.2, 0.2);
const r10 = displayPointRadiusWorld({ pointRadiusBasePx: 6 }, 2.0, 0.2);
// On-screen pixel size grows by scale^α (α = 0.33): 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 pointRadiusBasePx is set", () => {
const r = displayPointRadiusWorld(
{ pointRadiusPx: 99, pointRadiusBasePx: 4 },
0.4,
0.2,
);
// On-screen pixel size: 4 * (0.4 / 0.2)^α = 4 * 2^0.33
// In world units: (4 * 2^0.33) / 0.4.
const expected = (4 * Math.pow(2, PLANET_SIZE_ZOOM_ALPHA)) / 0.4;
expect(r).toBeCloseTo(expected, 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);
});
});