feat: Refactor draw-relief-icons.ts to integrate with WebGL2LayerFramework

- Implemented registration of draw-relief-icons with WebGL2LayerFramework, removing module-level renderer state.
- Updated drawRelief, undrawRelief, and rerenderReliefIcons functions to utilize framework methods.
- Ensured SVG fallback path is preserved and functional.
- Added performance criteria for rendering relief icons.
- Created tests to verify fallback integration and visual parity with existing SVG output.

test: Add WebGL2 fallback integration verification

- Introduced new tests for WebGL2LayerFramework to ensure no-ops when fallback is active.
- Verified that drawRelief routes to SVG when WebGL2 is unavailable.
- Confirmed visual parity between SVG output and existing implementation.
- Ensured all tests pass with updated coverage metrics.
This commit is contained in:
Azgaar 2026-03-12 14:28:33 +01:00
parent 8c78fe2ec1
commit 30f74373b8
5 changed files with 1154 additions and 20 deletions

View file

@ -1,7 +1,5 @@
import { Group, OrthographicCamera, Scene, WebGLRenderer } from "three";
// ─── Pure exports (testable without DOM or WebGL) ────────────────────────────
/**
* Converts a D3 zoom transform into orthographic camera bounds.
*
@ -83,8 +81,6 @@ interface RegisteredLayer {
group: Group; // framework-owned; passed to all callbacks — abstraction boundary
}
// ─── Class ───────────────────────────────────────────────────────────────────
export class WebGL2LayerFrameworkClass {
private canvas: HTMLCanvasElement | null = null;
private renderer: WebGLRenderer | null = null;
@ -95,18 +91,12 @@ export class WebGL2LayerFrameworkClass {
private resizeObserver: ResizeObserver | null = null;
private rafId: number | null = null;
private container: HTMLElement | null = null;
// Backing field — MUST NOT be declared readonly.
// readonly fields can only be assigned in the constructor; init() sets _fallback
// post-construction, which would cause a TypeScript type error with readonly.
private _fallback = false;
get hasFallback(): boolean {
return this._fallback;
}
// ─── Public API ────────────────────────────────────────────────────────────
init(): boolean {
this._fallback = !detectWebGL2();
if (this._fallback) return false;
@ -168,7 +158,6 @@ export class WebGL2LayerFrameworkClass {
this.layers.set(config.id, { config, group });
}
this.pendingConfigs = [];
this.observeResize();
return true;
@ -248,8 +237,6 @@ export class WebGL2LayerFrameworkClass {
camera.updateProjectionMatrix();
}
// ─── Private helpers ───────────────────────────────────────────────────────
private subscribeD3Zoom(): void {
// viewbox is a D3 selection global available in the browser; guard for Node test env
if (typeof (globalThis as any).viewbox === "undefined") return;
@ -283,9 +270,6 @@ export class WebGL2LayerFrameworkClass {
}
}
// ─── Global registration (MUST be last line) ─────────────────────────────────
// Uses globalThis (≡ window in browsers) to support both browser runtime and
// Node.js test environments without a ReferenceError.
declare global {
var WebGL2LayerFramework: WebGL2LayerFrameworkClass;
}