mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 19:04:02 +00:00
refactor(street-level): remove the remaining duplicated code and fakes
A duplicate scan of every module the PR adds (repeated 4-line blocks, normalised) after gekh's dedup round left these: - glyphs.js: the cone, ring and position glyphs share cachedGlyph(), wedge(), ring() and dot(). Checked with a recording canvas: all 20 draw calls keep the same path and fill/stroke state, so the glyphs are pixel-identical. - panelPositionControls.js: one placeAt() for the three places that pin a panel's top-left corner as a fixed window. - tiles.js: one warn() for the three background-failure logs. - Tests: src/testSupport/streetLevelFakes.mjs holds the provider snapshot (uiState + presentation tests), the stand-in provider (core + controls tests) and one real-ray pinhole camera (view + coverage tests, which had each written their own). The coverage line-of-sight test still fails when refresh() stops passing its focus. - qa-street-level.mjs: one waitForCoverage() for the five copies of the "on, drawn, settled, not key-gated" wait. No behaviour change: unit 5862 pass, fixture gate 16/16, panel gate 5/5. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
d70f468781
commit
c2f1b54d00
12 files changed
+281
-345
No files matched your search
@@ -6,81 +6,76 @@
|
||||
|
||||
const _cache = new Map();
|
||||
|
||||
function canvas(size) {
|
||||
/** A square canvas glyph drawn once per key, centred at (c, c). */
|
||||
function cachedGlyph(key, size, draw) {
|
||||
if (_cache.has(key)) return _cache.get(key);
|
||||
const element = document.createElement('canvas');
|
||||
element.width = size;
|
||||
element.height = size;
|
||||
draw(element.getContext('2d'), size / 2);
|
||||
_cache.set(key, element);
|
||||
return element;
|
||||
}
|
||||
|
||||
/** A filled wedge from the centre, pointing "up", `halfAngle` radians wide. */
|
||||
function wedge(ctx, c, radius, halfAngle, color, alpha) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(c, c);
|
||||
ctx.arc(c, c, radius, -Math.PI / 2 - halfAngle, -Math.PI / 2 + halfAngle);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = color;
|
||||
ctx.globalAlpha = alpha;
|
||||
ctx.fill();
|
||||
ctx.globalAlpha = 1;
|
||||
}
|
||||
|
||||
/** A stroked circle around the centre. */
|
||||
function ring(ctx, c, radius, color, width, alpha) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(c, c, radius, 0, Math.PI * 2);
|
||||
ctx.strokeStyle = color;
|
||||
ctx.lineWidth = width;
|
||||
ctx.globalAlpha = alpha;
|
||||
ctx.stroke();
|
||||
ctx.globalAlpha = 1;
|
||||
}
|
||||
|
||||
/** A filled dot at the centre with a dark outline. */
|
||||
function dot(ctx, c, radius, color, outline, width) {
|
||||
ctx.beginPath();
|
||||
ctx.arc(c, c, radius, 0, Math.PI * 2);
|
||||
ctx.fillStyle = color;
|
||||
ctx.fill();
|
||||
ctx.lineWidth = width;
|
||||
ctx.strokeStyle = outline;
|
||||
ctx.stroke();
|
||||
}
|
||||
|
||||
/** Cone glyph pointing "up"; rotate the billboard by the compass angle. */
|
||||
export function imageConeGlyph({
|
||||
size = 32,
|
||||
color = '#e8eaed',
|
||||
pano = false,
|
||||
} = {}) {
|
||||
const key = `cone:${size}:${color}:${pano}`;
|
||||
if (_cache.has(key)) return _cache.get(key);
|
||||
const element = canvas(size);
|
||||
const ctx = element.getContext('2d');
|
||||
const c = size / 2;
|
||||
if (!pano) {
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(c, c);
|
||||
ctx.arc(c, c, size * 0.46, -Math.PI / 2 - 0.62, -Math.PI / 2 + 0.62);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = color;
|
||||
ctx.globalAlpha = 0.42;
|
||||
ctx.fill();
|
||||
ctx.globalAlpha = 1;
|
||||
} else {
|
||||
ctx.beginPath();
|
||||
ctx.arc(c, c, size * 0.4, 0, Math.PI * 2);
|
||||
ctx.strokeStyle = color;
|
||||
ctx.globalAlpha = 0.5;
|
||||
ctx.lineWidth = Math.max(1.5, size * 0.09);
|
||||
ctx.stroke();
|
||||
ctx.globalAlpha = 1;
|
||||
}
|
||||
ctx.beginPath();
|
||||
ctx.arc(c, c, size * 0.16, 0, Math.PI * 2);
|
||||
ctx.fillStyle = color;
|
||||
ctx.fill();
|
||||
ctx.lineWidth = Math.max(1, size * 0.05);
|
||||
ctx.strokeStyle = 'rgba(10,10,15,0.85)';
|
||||
ctx.stroke();
|
||||
_cache.set(key, element);
|
||||
return element;
|
||||
return cachedGlyph(`cone:${size}:${color}:${pano}`, size, (ctx, c) => {
|
||||
if (pano) ring(ctx, c, size * 0.4, color, Math.max(1.5, size * 0.09), 0.5);
|
||||
else wedge(ctx, c, size * 0.46, 0.62, color, 0.42);
|
||||
dot(
|
||||
ctx,
|
||||
c,
|
||||
size * 0.16,
|
||||
color,
|
||||
'rgba(10,10,15,0.85)',
|
||||
Math.max(1, size * 0.05),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/** Pulsing-looking marker for the image the viewer currently shows. */
|
||||
export function positionMarkerGlyph({ size = 44, color = '#ffb300' } = {}) {
|
||||
const key = `pos:${size}:${color}`;
|
||||
if (_cache.has(key)) return _cache.get(key);
|
||||
const element = canvas(size);
|
||||
const ctx = element.getContext('2d');
|
||||
const c = size / 2;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(c, c);
|
||||
ctx.arc(c, c, size * 0.48, -Math.PI / 2 - 0.55, -Math.PI / 2 + 0.55);
|
||||
ctx.closePath();
|
||||
ctx.fillStyle = color;
|
||||
ctx.globalAlpha = 0.55;
|
||||
ctx.fill();
|
||||
ctx.globalAlpha = 1;
|
||||
ctx.beginPath();
|
||||
ctx.arc(c, c, size * 0.2, 0, Math.PI * 2);
|
||||
ctx.fillStyle = color;
|
||||
ctx.fill();
|
||||
ctx.lineWidth = 2;
|
||||
ctx.strokeStyle = '#0a0a0f';
|
||||
ctx.stroke();
|
||||
ctx.beginPath();
|
||||
ctx.arc(c, c, size * 0.3, 0, Math.PI * 2);
|
||||
ctx.strokeStyle = color;
|
||||
ctx.lineWidth = 1.5;
|
||||
ctx.globalAlpha = 0.8;
|
||||
ctx.stroke();
|
||||
_cache.set(key, element);
|
||||
return element;
|
||||
return cachedGlyph(`pos:${size}:${color}`, size, (ctx, c) => {
|
||||
wedge(ctx, c, size * 0.48, 0.55, color, 0.55);
|
||||
dot(ctx, c, size * 0.2, color, '#0a0a0f', 2);
|
||||
ring(ctx, c, size * 0.3, color, 1.5, 0.8);
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user