Files
galaxy-game/ui/frontend/src/map/fixtures.ts
T
Ilia Denisov db415f8aa4 ui/phase-9: PixiJS map renderer with torus and no-wrap modes
Stand up the vector map renderer in ui/frontend/src/map/ on top of
PixiJS v8 + pixi-viewport@^6. Torus mode renders nine container
copies for seamless wrap; no-wrap mode pins the camera at world
bounds and centres on an axis when the viewport exceeds the world
along that axis. Hit-test is a brute-force pass with deterministic
[-priority, distSq, kindOrder, id] ordering and torus-shortest
distance, validated by hand-built unit cases.

The development playground at /__debug/map exposes a window
debug surface for the Playwright spec, which forces WebGPU on
chromium-desktop, WebGL on webkit-desktop, and accepts the
auto-picked backend on mobile projects.

Algorithm spec lives in ui/docs/renderer.md, which also pins the
new deprecation status of galaxy/client (the entire Fyne client
module, including client/world). client/world/README.md and the
Phase 9 stub in ui/PLAN.md gain matching deprecation banners.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-08 14:06:23 +02:00

101 lines
2.5 KiB
TypeScript

// Fixture data for the map renderer playground and visual checks.
//
// sampleWorld() returns a 1000-primitive deterministic world built
// with a small linear-congruential RNG so the layout is reproducible
// across runs and across machines. The mix of primitive kinds
// exercises all draw paths: many points (planets), several stroked
// circles (orbits), several filled circles (zones), and a handful of
// lines (routes).
import {
type CirclePrim,
type LinePrim,
type PointPrim,
type Primitive,
World,
} from "./world";
const WORLD_W = 4000;
const WORLD_H = 4000;
// Tiny deterministic RNG so fixtures stay byte-identical regardless
// of host platform. Seed values picked to give a visually pleasant
// distribution; not cryptographically meaningful.
function lcg(seed: number): () => number {
let s = seed >>> 0;
return () => {
s = (Math.imul(s, 1664525) + 1013904223) >>> 0;
return s / 0x1_0000_0000;
};
}
// sampleWorld constructs the playground world. The result is stable
// across calls — it allocates fresh arrays but the data is identical.
export function sampleWorld(): World {
const rand = lcg(0x5eed1234);
const primitives: Primitive[] = [];
let nextId = 0;
// 950 stars (points).
for (let i = 0; i < 950; i++) {
const star: PointPrim = {
kind: "point",
id: nextId++,
x: rand() * WORLD_W,
y: rand() * WORLD_H,
priority: 1,
style: { pointRadiusPx: 2 + Math.floor(rand() * 3) },
hitSlopPx: 0,
};
primitives.push(star);
}
// 30 stroked circles (orbits / influence rings).
for (let i = 0; i < 30; i++) {
const orbit: CirclePrim = {
kind: "circle",
id: nextId++,
x: rand() * WORLD_W,
y: rand() * WORLD_H,
radius: 80 + rand() * 220,
priority: 2,
style: { strokeWidthPx: 1, strokeAlpha: 0.6 },
hitSlopPx: 0,
};
primitives.push(orbit);
}
// 10 filled translucent circles (zones).
for (let i = 0; i < 10; i++) {
const zone: CirclePrim = {
kind: "circle",
id: nextId++,
x: rand() * WORLD_W,
y: rand() * WORLD_H,
radius: 150 + rand() * 250,
priority: 0,
style: { fillColor: 0x37474f, fillAlpha: 0.25 },
hitSlopPx: 0,
};
primitives.push(zone);
}
// 10 lines (routes between random anchor points).
for (let i = 0; i < 10; i++) {
const route: LinePrim = {
kind: "line",
id: nextId++,
x1: rand() * WORLD_W,
y1: rand() * WORLD_H,
x2: rand() * WORLD_W,
y2: rand() * WORLD_H,
priority: 3,
style: { strokeWidthPx: 1, strokeAlpha: 0.8 },
hitSlopPx: 0,
};
primitives.push(route);
}
return new World(WORLD_W, WORLD_H, primitives);
}