polish: scene nits — live-lens yard, square stacks, deck-clear billboard, two-layer lamps, flare reach, phone traffic and exposure
This commit is contained in:
@@ -2,7 +2,15 @@ import { readFileSync } from 'node:fs';
|
||||
import { resolve } from 'node:path';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { SceneResourceBag } from '@/scene/resources';
|
||||
import { BEAM_INTENSITY, createRelayMasts, RUNG_FLASH_GAP_SECONDS, relayLayout } from '@/scene/world/relay-masts';
|
||||
import {
|
||||
BEAM_INTENSITY,
|
||||
createRelayMasts,
|
||||
RUNG_CORE,
|
||||
RUNG_FLASH_GAP_SECONDS,
|
||||
RUNG_HALO,
|
||||
RUNG_HALO_GAIN,
|
||||
relayLayout,
|
||||
} from '@/scene/world/relay-masts';
|
||||
import { publishRelay } from '@/journey/relay-progress';
|
||||
import { readSwitchboard } from '@/audio/music-pulse';
|
||||
import { playRelayTone } from '@/audio/relay-tone';
|
||||
@@ -54,6 +62,35 @@ describe('relay mast layout', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('portrait: one large transmitter carries the rungs of every group, bottom-up in group order', () => {
|
||||
const masts = relayLayout(0.5, counts, false, 390 / 844);
|
||||
expect(masts).toHaveLength(4);
|
||||
expect(masts.filter((mast) => mast.shown)).toHaveLength(1);
|
||||
const [stand] = masts;
|
||||
// One stand, and it is big: well over twice the design mast.
|
||||
masts.forEach((mast) => expect([mast.x, mast.z, mast.scale]).toEqual([stand!.x, stand!.z, stand!.scale]));
|
||||
expect(stand!.height).toBeGreaterThan(2 * relayLayout(0.5, counts)[0]!.height);
|
||||
// On screen, left of the road and inside the frame (portrait lens: no shift).
|
||||
const tanHalfWidth = Math.tan((62 / 2) * (Math.PI / 180)) * (390 / 844);
|
||||
const fx = stand!.x / (tanHalfWidth * (25 - stand!.z));
|
||||
expect(fx).toBeLessThan(-0.2);
|
||||
expect(fx).toBeGreaterThan(-0.7);
|
||||
// Rungs: one per technology, strictly rising through the groups, inside the mast.
|
||||
const ys = masts.flatMap((mast) => mast.rungs.map((rung) => rung.y));
|
||||
expect(ys).toHaveLength(counts.reduce((sum, count) => sum + count, 0));
|
||||
ys.slice(1).forEach((y, i) => expect(y).toBeGreaterThan(ys[i]!));
|
||||
expect(ys.at(-1)!).toBeLessThan(stand!.height);
|
||||
});
|
||||
|
||||
it('keeps lit rung cores under the tone shoulder and bloom threshold, violet not white', () => {
|
||||
const luminance = (c: readonly number[]) => 0.2126 * c[0]! + 0.7152 * c[1]! + 0.0722 * c[2]!;
|
||||
expect(luminance(RUNG_CORE)).toBeLessThan(0.3);
|
||||
expect(luminance(RUNG_CORE.map((v, i) => v + RUNG_HALO[i]! * RUNG_HALO_GAIN))).toBeLessThan(0.85);
|
||||
// Saturated: blue dominates, green is a small fraction (lavender-white would be near-equal channels).
|
||||
expect(RUNG_CORE[1] / RUNG_CORE[2]).toBeLessThan(0.2);
|
||||
expect(RUNG_HALO[1] / RUNG_HALO[2]).toBeLessThan(0.2);
|
||||
});
|
||||
|
||||
it('is absent outside the chapter and fades at its edges', () => {
|
||||
expect(relayLayout(null, counts).every((mast) => mast.presence === 0)).toBe(true);
|
||||
expect(relayLayout(0, counts).every((mast) => mast.presence === 0)).toBe(true);
|
||||
|
||||
Reference in New Issue
Block a user