diff --git a/package.json b/package.json index 820cafe6..1d80af51 100644 --- a/package.json +++ b/package.json @@ -30,6 +30,7 @@ "@types/delaunator": "^5.0.3", "@types/node": "^25.0.10", "@types/polylabel": "^1.1.3", + "@types/three": "^0.183.1", "@vitest/browser": "^4.0.18", "@vitest/browser-playwright": "^4.0.18", "playwright": "^1.57.0", @@ -38,7 +39,6 @@ "vitest": "^4.0.18" }, "dependencies": { - "@types/three": "^0.183.1", "alea": "^1.0.1", "d3": "^7.9.0", "delaunator": "^5.0.1", diff --git a/public/modules/ui/relief-editor.js b/public/modules/ui/relief-editor.js index 062350c1..88e3f7af 100644 --- a/public/modules/ui/relief-editor.js +++ b/public/modules/ui/relief-editor.js @@ -4,10 +4,12 @@ function editReliefIcon() { closeDialogs(".stable"); // Switch from WebGL to editable SVG elements - undrawRelief(); - drawRelief("svg"); + if (!layerIsOn("toggleRelief")) { + undrawRelief(); + turnButtonOn("toggleRelief"); + drawRelief("svg"); + } - if (!layerIsOn("toggleRelief")) toggleRelief(); terrain.selectAll("use").call(d3.drag().on("drag", dragReliefIcon)).classed("draggable", true); // Click-to-select: delegation on the terrain group covers existing and newly added elements. diff --git a/public/modules/ui/tools.js b/public/modules/ui/tools.js index b6398c6e..9ef8a1f8 100644 --- a/public/modules/ui/tools.js +++ b/public/modules/ui/tools.js @@ -80,7 +80,7 @@ function processFeatureRegeneration(event, button) { drawStateLabels(); } else if (button === "regenerateReliefIcons") { generateReliefIcons(); - if (!layerIsOn("toggleRelief")) toggleRelief(); + layerIsOn("toggleRelief") ? drawRelief() : toggleRelief(); } else if (button === "regenerateRoutes") { regenerateRoutes(); if (!layerIsOn("toggleRoutes")) toggleRoutes(); diff --git a/src/renderers/draw-relief-icons.ts b/src/renderers/draw-relief-icons.ts index 3f82b79b..40dafece 100644 --- a/src/renderers/draw-relief-icons.ts +++ b/src/renderers/draw-relief-icons.ts @@ -5,18 +5,19 @@ import { generateRelief } from "../modules/relief-generator"; import { byId } from "../utils"; let fo: SVGForeignObjectElement | null = null; -let renderer: any = null; // THREE.WebGLRenderer -let camera: any = null; // THREE.OrthographicCamera -let scene: any = null; // THREE.Scene +let renderer: THREE.WebGLRenderer | null = null; +let camera: THREE.OrthographicCamera | null = null; +let scene: THREE.Scene | null = null; -const textureCache = new Map(); // set name → THREE.Texture +const textureCache = new Map(); // set name → THREE.Texture function preloadTextures(): void { for (const set of Object.keys(RELIEF_SYMBOLS)) loadTexture(set); } -function loadTexture(set: string): Promise { - if (textureCache.has(set)) return Promise.resolve(textureCache.get(set)); +function loadTexture(set: string): Promise { + if (textureCache.has(set)) + return Promise.resolve(textureCache.get(set) || null); return new Promise((resolve) => { const loader = new THREE.TextureLoader(); loader.load( @@ -179,10 +180,13 @@ function disposeTextureCache(): void { function disposeScene(): void { if (!scene) return; while (scene.children.length) { - const mesh = scene.children[0]; + const mesh = scene.children[0] as THREE.Mesh< + THREE.BufferGeometry, + THREE.Material + >; scene.remove(mesh); mesh.geometry?.dispose(); - if (mesh.material) { + if (mesh.material && "map" in mesh.material) { mesh.material.map = null; mesh.material.dispose(); }