feat: Enhance WebGL2LayerFramework initialization and improve global variable handling

This commit is contained in:
Azgaar 2026-03-12 18:25:08 +01:00
parent 2dae325d05
commit 8560c131eb
6 changed files with 202 additions and 147 deletions

View file

@ -1,68 +1,61 @@
<!doctype html>
<html lang="en">
<head>
<head>
<title>Code coverage report for webgl-layer-framework.ts</title>
<meta charset="utf-8" />
<link rel="stylesheet" href="prettify.css" />
<link rel="stylesheet" href="base.css" />
<link rel="shortcut icon" type="image/x-icon" href="favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<style type='text/css'>
.coverage-summary .sorter {
background-image: url(sort-arrow-sprite.png);
}
<style type="text/css">
.coverage-summary .sorter {
background-image: url(sort-arrow-sprite.png);
}
</style>
</head>
<body>
<div class='wrapper'>
<div class='pad1'>
</head>
<body>
<div class="wrapper">
<div class="pad1">
<h1><a href="index.html">All files</a> webgl-layer-framework.ts</h1>
<div class='clearfix'>
<div class='fl pad1y space-right2'>
<span class="strong">88.51% </span>
<span class="quiet">Statements</span>
<span class='fraction'>131/148</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">76.82% </span>
<span class="quiet">Branches</span>
<span class='fraction'>63/82</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">84.21% </span>
<span class="quiet">Functions</span>
<span class='fraction'>16/19</span>
</div>
<div class='fl pad1y space-right2'>
<span class="strong">91.26% </span>
<span class="quiet">Lines</span>
<span class='fraction'>115/126</span>
</div>
<div class="clearfix">
<div class="fl pad1y space-right2">
<span class="strong">88.51% </span>
<span class="quiet">Statements</span>
<span class="fraction">131/148</span>
</div>
<div class="fl pad1y space-right2">
<span class="strong">76.82% </span>
<span class="quiet">Branches</span>
<span class="fraction">63/82</span>
</div>
<div class="fl pad1y space-right2">
<span class="strong">84.21% </span>
<span class="quiet">Functions</span>
<span class="fraction">16/19</span>
</div>
<div class="fl pad1y space-right2">
<span class="strong">91.26% </span>
<span class="quiet">Lines</span>
<span class="fraction">115/126</span>
</div>
</div>
<p class="quiet">
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the previous block.
Press <em>n</em> or <em>j</em> to go to the next uncovered block, <em>b</em>, <em>p</em> or <em>k</em> for the
previous block.
</p>
<template id="filterTemplate">
<div class="quiet">
Filter:
<input type="search" id="fileSearch">
</div>
<div class="quiet">
Filter:
<input type="search" id="fileSearch" />
</div>
</template>
</div>
<div class='status-line high'></div>
<pre><table class="coverage">
</div>
<div class="status-line high"></div>
<pre><table class="coverage">
<tr><td class="line-count quiet"><a name='L1'></a><a href='#L1'>1</a>
<a name='L2'></a><a href='#L2'>2</a>
<a name='L3'></a><a href='#L3'>3</a>
@ -835,11 +828,6 @@ export class WebGL2LayerFrameworkClass {
syncTransform(): void {
if (this._fallback || !this.camera) return;
const camera = this.camera;
const viewX = (globalThis as any).viewX ?? 0;
const viewY = (globalThis as any).viewY ?? 0;
const scale = (globalThis as any).scale ?? 1;
const graphWidth = (globalThis as any).graphWidth ?? 960;
const graphHeight = (globalThis as any).graphHeight ?? 540;
const bounds = buildCameraBounds(
viewX,
viewY,
@ -893,21 +881,22 @@ declare global {
globalThis.WebGL2LayerFramework = new WebGL2LayerFrameworkClass();
&nbsp;</pre></td></tr></table></pre>
<div class='push'></div><!-- for sticky footer -->
</div><!-- /wrapper -->
<div class='footer quiet pad2 space-top1 center small'>
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-03-12T13:47:51.911Z
</div>
<script src="prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="sorter.js"></script>
<script src="block-navigation.js"></script>
</body>
<div class="push"></div>
<!-- for sticky footer -->
</div>
<!-- /wrapper -->
<div class="footer quiet pad2 space-top1 center small">
Code coverage generated by
<a href="https://istanbul.js.org/" target="_blank" rel="noopener noreferrer">istanbul</a>
at 2026-03-12T13:47:51.911Z
</div>
<script src="prettify.js"></script>
<script>
window.onload = function () {
prettyPrint();
};
</script>
<script src="sorter.js"></script>
<script src="block-navigation.js"></script>
</body>
</html>

View file

@ -91,25 +91,11 @@ export class WebGL2LayerFrameworkClass {
private resizeObserver: ResizeObserver | null = null;
private rafId: number | null = null;
private container: HTMLElement | null = null;
private _fallback = false;
get hasFallback(): boolean {
return this._fallback;
}
init(): boolean {
this._fallback = !detectWebGL2();
if (this._fallback) return false;
const mapEl = document.getElementById("map");
if (!mapEl) {
console.warn(
"WebGL2LayerFramework: #map element not found — init() aborted",
);
return false;
}
if (!mapEl) throw new Error("Map element not found");
// Wrap #map in a positioned container so the canvas can be a sibling with z-index
const container = document.createElement("div");
container.id = "map-container";
container.style.position = "relative";
@ -117,7 +103,6 @@ export class WebGL2LayerFrameworkClass {
container.appendChild(mapEl);
this.container = container;
// Canvas: sibling to #map, pointerless, z-index above SVG (AC1)
const canvas = document.createElement("canvas");
canvas.id = "terrainCanvas";
canvas.style.position = "absolute";
@ -125,27 +110,20 @@ export class WebGL2LayerFrameworkClass {
canvas.style.pointerEvents = "none";
canvas.setAttribute("aria-hidden", "true");
canvas.style.zIndex = String(getLayerZIndex("terrain"));
canvas.width = container.clientWidth || 960;
canvas.height = container.clientHeight || 540;
canvas.width = mapEl.clientWidth || 960;
canvas.height = mapEl.clientHeight || 540;
container.appendChild(canvas);
this.canvas = canvas;
// Three.js core objects (AC4)
this.renderer = new WebGLRenderer({
canvas,
antialias: false,
antialias: true,
alpha: true,
});
this.renderer.setSize(canvas.width, canvas.height);
this.renderer.setPixelRatio(window.devicePixelRatio);
this.renderer.setSize(canvas.width, canvas.height, false);
this.scene = new Scene();
this.camera = new OrthographicCamera(
0,
canvas.width,
0,
canvas.height,
-1,
1,
);
this.camera = new OrthographicCamera(0, graphWidth, 0, graphHeight, -1, 1);
this.subscribeD3Zoom();
@ -178,7 +156,6 @@ export class WebGL2LayerFrameworkClass {
}
unregister(id: string): void {
if (this._fallback) return;
const layer = this.layers.get(id);
if (!layer || !this.scene) return;
const scene = this.scene;
@ -190,7 +167,6 @@ export class WebGL2LayerFrameworkClass {
}
setVisible(id: string, visible: boolean): void {
if (this._fallback) return;
const layer = this.layers.get(id);
if (!layer) return;
layer.group.visible = visible;
@ -200,14 +176,13 @@ export class WebGL2LayerFrameworkClass {
}
clearLayer(id: string): void {
if (this._fallback) return;
const layer = this.layers.get(id);
if (!layer) return;
layer.group.clear();
this.requestRender();
}
requestRender(): void {
if (this._fallback) return;
if (this.rafId !== null) return;
this.rafId = requestAnimationFrame(() => {
this.rafId = null;
@ -216,13 +191,7 @@ export class WebGL2LayerFrameworkClass {
}
syncTransform(): void {
if (this._fallback || !this.camera) return;
const camera = this.camera;
const viewX = (globalThis as any).viewX ?? 0;
const viewY = (globalThis as any).viewY ?? 0;
const scale = (globalThis as any).scale ?? 1;
const graphWidth = (globalThis as any).graphWidth ?? 960;
const graphHeight = (globalThis as any).graphHeight ?? 540;
const bounds = buildCameraBounds(
viewX,
viewY,
@ -238,39 +207,38 @@ export class WebGL2LayerFrameworkClass {
}
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;
(globalThis as any).viewbox.on("zoom.webgl", () => this.requestRender());
// viewbox is declared as a global in src/types/global.ts (exposed by main.js).
// Guard for Node test env where it doesn't exist.
if (typeof viewbox === "undefined") return;
viewbox.on("zoom.webgl", () => this.requestRender());
}
private observeResize(): void {
if (!this.container || !this.renderer) return;
const mapEl = this.container.querySelector("#map") ?? this.container;
this.resizeObserver = new ResizeObserver((entries) => {
const { width, height } = entries[0].contentRect;
if (this.renderer && this.canvas) {
this.renderer.setSize(width, height);
if (this.renderer && this.canvas && width > 0 && height > 0) {
// updateStyle=false — CSS inset:0 handles canvas positioning.
this.renderer.setSize(width, height, false);
this.requestRender();
}
});
this.resizeObserver.observe(this.container);
this.resizeObserver.observe(mapEl);
}
private render(): void {
if (this._fallback || !this.renderer || !this.scene || !this.camera) return;
const renderer = this.renderer;
const scene = this.scene;
const camera = this.camera;
if (!this.renderer || !this.scene || !this.camera) return;
this.syncTransform();
for (const layer of this.layers.values()) {
if (layer.group.visible) {
layer.config.render(layer.group);
}
if (layer.group.visible) layer.config.render(layer.group);
}
renderer.render(scene, camera);
this.renderer.render(this.scene, this.camera);
}
}
declare global {
var WebGL2LayerFramework: WebGL2LayerFrameworkClass;
}
globalThis.WebGL2LayerFramework = new WebGL2LayerFrameworkClass();
window.WebGL2LayerFramework = new WebGL2LayerFrameworkClass();