90 lines
3.8 KiB
JavaScript
90 lines
3.8 KiB
JavaScript
import { TestCase } from "./framework/test_case.js";
|
|
import { MapTopology, mapToLocal } from "../shared/hex.js";
|
|
import { GLMapRenderer } from "../client/js/map_view/webgl.js";
|
|
import {
|
|
FOG_EXPLORED_OPACITY,
|
|
FOG_EXPLORED_RGB,
|
|
FOG_UNEXPLORED_OPACITY,
|
|
FOG_UNEXPLORED_RGB,
|
|
blurFog,
|
|
fogLevel,
|
|
isFogged,
|
|
} from "../client/js/map_view/fog.js";
|
|
|
|
// Two samples on opposite sides of a shared hex edge, halfway between the two
|
|
// tile centres.
|
|
const edgeSamples = (a, b) => [
|
|
{ level: a, dx: -23.625, dy: -14 },
|
|
{ level: b, dx: 23.625, dy: 14 },
|
|
];
|
|
|
|
export class FogTest extends TestCase {
|
|
test_levels_describe_visible_explored_and_unexplored_ground() {
|
|
this.assertFalse(isFogged(fogLevel(true, true)), "visible ground has no fog");
|
|
this.assertEqual(fogLevel(false, true).opacity, FOG_EXPLORED_OPACITY);
|
|
this.assertEqual(fogLevel(false, false).opacity, FOG_UNEXPLORED_OPACITY);
|
|
this.assertEqual(fogLevel(false, true).rgb, FOG_EXPLORED_RGB);
|
|
this.assertEqual(fogLevel(false, false).rgb, FOG_UNEXPLORED_RGB);
|
|
}
|
|
|
|
test_the_frontier_fades_instead_of_stepping() {
|
|
const explored = fogLevel(false, true);
|
|
const visible = fogLevel(true, true);
|
|
const blend = blurFog(edgeSamples(explored, visible));
|
|
// Half the kernel is over fogged ground, so the edge is about half as opaque
|
|
// as the explored tile rather than jumping straight to clear.
|
|
this.assertApprox(blend.opacity, FOG_EXPLORED_OPACITY / 2, 1e-6, "faded opacity");
|
|
this.assertEqual(blend.rgb, FOG_EXPLORED_RGB, "the fog keeps its colour");
|
|
}
|
|
|
|
test_a_tile_centre_keeps_its_own_fog() {
|
|
const unexplored = fogLevel(false, false);
|
|
const clear = fogLevel(true, true);
|
|
const blur = blurFog([
|
|
{ level: unexplored, dx: 0, dy: 0 },
|
|
{ level: clear, dx: 47.25, dy: 28 },
|
|
]);
|
|
this.assertGreater(blur.opacity, 0.9, "the middle of the fog stays opaque");
|
|
}
|
|
|
|
test_explored_and_unexplored_colours_mix() {
|
|
const blend = blurFog(edgeSamples(fogLevel(false, true), fogLevel(false, false)));
|
|
this.assertGreater(blend.opacity, FOG_EXPLORED_OPACITY, "between the two fog levels");
|
|
this.assertLess(blend.opacity, FOG_UNEXPLORED_OPACITY);
|
|
for (let i = 0; i < 3; i++) {
|
|
const low = Math.min(FOG_EXPLORED_RGB[i], FOG_UNEXPLORED_RGB[i]);
|
|
const high = Math.max(FOG_EXPLORED_RGB[i], FOG_UNEXPLORED_RGB[i]);
|
|
this.assertTrue(blend.rgb[i] >= low - 1e-9 && blend.rgb[i] <= high + 1e-9, `channel ${i} mixed`);
|
|
}
|
|
}
|
|
|
|
test_the_blur_agrees_from_both_sides_of_an_edge() {
|
|
// The renderer builds one ring per tile; both tiles on a shared edge must
|
|
// produce the same blurred fog there, or the frontier would seam.
|
|
const topology = new MapTopology({ x: 5, y: 5 }, false);
|
|
const renderer = Object.create(GLMapRenderer.prototype);
|
|
const levelAt = (x, y) => fogLevel(false, !(x === 0 && y === 0));
|
|
const centreA = mapToLocal(0, 0);
|
|
const centreB = mapToLocal(1, 0);
|
|
const ringA = renderer._fogRing(topology, 0, 0, centreA, levelAt);
|
|
const ringB = renderer._fogRing(topology, 1, 0, centreB, levelAt);
|
|
const px = (centreA.x + centreB.x) / 2;
|
|
const py = (centreA.y + centreB.y) / 2;
|
|
const samples = (ring) => ring.map((s) => ({ level: s.level, dx: s.x - px, dy: s.y - py }));
|
|
const a = blurFog(samples(ringA));
|
|
const b = blurFog(samples(ringB));
|
|
this.assertApprox(a.opacity, b.opacity, 1e-12, "the shared edge fades identically");
|
|
for (let i = 0; i < 3; i++) {
|
|
this.assertApprox(a.rgb[i], b.rgb[i], 1e-12, `channel ${i} agrees`);
|
|
}
|
|
}
|
|
|
|
test_off_map_neighbours_are_ignored() {
|
|
const blend = blurFog([
|
|
{ level: fogLevel(false, false), dx: 0, dy: 0 },
|
|
{ level: null, dx: 47.25, dy: 28 },
|
|
]);
|
|
this.assertApprox(blend.opacity, FOG_UNEXPLORED_OPACITY, 1e-6, "a missing tile does not dilute the fog");
|
|
}
|
|
}
|