@tonybfox/threejs-tools 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/README.md +321 -0
  2. package/dist/asset-loader/index.cjs +376 -0
  3. package/dist/asset-loader/index.cjs.map +1 -0
  4. package/dist/asset-loader/index.d.mts +101 -0
  5. package/dist/asset-loader/index.d.ts +101 -0
  6. package/dist/asset-loader/index.mjs +7 -0
  7. package/dist/asset-loader/index.mjs.map +1 -0
  8. package/dist/camera/index.cjs +313 -0
  9. package/dist/camera/index.cjs.map +1 -0
  10. package/dist/camera/index.d.mts +82 -0
  11. package/dist/camera/index.d.ts +82 -0
  12. package/dist/camera/index.mjs +7 -0
  13. package/dist/camera/index.mjs.map +1 -0
  14. package/dist/chunk-5DP6WDB3.mjs +1161 -0
  15. package/dist/chunk-5DP6WDB3.mjs.map +1 -0
  16. package/dist/chunk-BJKSICFA.mjs +1579 -0
  17. package/dist/chunk-BJKSICFA.mjs.map +1 -0
  18. package/dist/chunk-BYRZCHE7.mjs +277 -0
  19. package/dist/chunk-BYRZCHE7.mjs.map +1 -0
  20. package/dist/chunk-EIROAPF7.mjs +387 -0
  21. package/dist/chunk-EIROAPF7.mjs.map +1 -0
  22. package/dist/chunk-EQDOX34V.mjs +164 -0
  23. package/dist/chunk-EQDOX34V.mjs.map +1 -0
  24. package/dist/chunk-IIAZ2WJJ.mjs +405 -0
  25. package/dist/chunk-IIAZ2WJJ.mjs.map +1 -0
  26. package/dist/chunk-L4VIIJZD.mjs +340 -0
  27. package/dist/chunk-L4VIIJZD.mjs.map +1 -0
  28. package/dist/chunk-P35QJCOG.mjs +339 -0
  29. package/dist/chunk-P35QJCOG.mjs.map +1 -0
  30. package/dist/chunk-R64RVBRM.mjs +394 -0
  31. package/dist/chunk-R64RVBRM.mjs.map +1 -0
  32. package/dist/compass/index.cjs +375 -0
  33. package/dist/compass/index.cjs.map +1 -0
  34. package/dist/compass/index.d.mts +58 -0
  35. package/dist/compass/index.d.ts +58 -0
  36. package/dist/compass/index.mjs +7 -0
  37. package/dist/compass/index.mjs.map +1 -0
  38. package/dist/grid/index.cjs +200 -0
  39. package/dist/grid/index.cjs.map +1 -0
  40. package/dist/grid/index.d.mts +43 -0
  41. package/dist/grid/index.d.ts +43 -0
  42. package/dist/grid/index.mjs +7 -0
  43. package/dist/grid/index.mjs.map +1 -0
  44. package/dist/index.cjs +5049 -0
  45. package/dist/index.cjs.map +1 -0
  46. package/dist/index.d.mts +13 -0
  47. package/dist/index.d.ts +13 -0
  48. package/dist/index.mjs +47 -0
  49. package/dist/index.mjs.map +1 -0
  50. package/dist/measurements/index.cjs +1198 -0
  51. package/dist/measurements/index.cjs.map +1 -0
  52. package/dist/measurements/index.d.mts +449 -0
  53. package/dist/measurements/index.d.ts +449 -0
  54. package/dist/measurements/index.mjs +9 -0
  55. package/dist/measurements/index.mjs.map +1 -0
  56. package/dist/sunlight/index.cjs +441 -0
  57. package/dist/sunlight/index.cjs.map +1 -0
  58. package/dist/sunlight/index.d.mts +92 -0
  59. package/dist/sunlight/index.d.ts +92 -0
  60. package/dist/sunlight/index.mjs +7 -0
  61. package/dist/sunlight/index.mjs.map +1 -0
  62. package/dist/terrain/index.cjs +423 -0
  63. package/dist/terrain/index.cjs.map +1 -0
  64. package/dist/terrain/index.d.mts +219 -0
  65. package/dist/terrain/index.d.ts +219 -0
  66. package/dist/terrain/index.mjs +7 -0
  67. package/dist/terrain/index.mjs.map +1 -0
  68. package/dist/transform-controls/index.cjs +1587 -0
  69. package/dist/transform-controls/index.cjs.map +1 -0
  70. package/dist/transform-controls/index.d.mts +162 -0
  71. package/dist/transform-controls/index.d.ts +162 -0
  72. package/dist/transform-controls/index.mjs +13 -0
  73. package/dist/transform-controls/index.mjs.map +1 -0
  74. package/dist/view-helper/index.cjs +430 -0
  75. package/dist/view-helper/index.cjs.map +1 -0
  76. package/dist/view-helper/index.d.mts +75 -0
  77. package/dist/view-helper/index.d.ts +75 -0
  78. package/dist/view-helper/index.mjs +7 -0
  79. package/dist/view-helper/index.mjs.map +1 -0
  80. package/package.json +124 -0
@@ -0,0 +1,7 @@
1
+ import {
2
+ SunLightTool
3
+ } from "../chunk-IIAZ2WJJ.mjs";
4
+ export {
5
+ SunLightTool
6
+ };
7
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,423 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // packages/terrain/src/index.ts
31
+ var src_exports = {};
32
+ __export(src_exports, {
33
+ TerrainTool: () => TerrainTool
34
+ });
35
+ module.exports = __toCommonJS(src_exports);
36
+
37
+ // packages/terrain/src/TerrainTool.ts
38
+ var THREE = __toESM(require("three"));
39
+ var TerrainTool = class extends THREE.EventDispatcher {
40
+ /**
41
+ * Creates a new TerrainTool instance
42
+ * @param scene - The Three.js scene to add terrain to
43
+ * @param options - Configuration options
44
+ */
45
+ constructor(scene, options = {}) {
46
+ super();
47
+ this.mesh = null;
48
+ this.currentData = null;
49
+ this.isLoading = false;
50
+ this.generatedTextureUrls = /* @__PURE__ */ new Set();
51
+ this.scene = scene;
52
+ this.widthSegments = options.widthSegments ?? 50;
53
+ this.depthSegments = options.depthSegments ?? 50;
54
+ this.elevationScale = options.elevationScale ?? 1;
55
+ this.baseColor = options.baseColor ?? 9139029;
56
+ this.wireframe = options.wireframe ?? false;
57
+ this.textureUrl = options.textureUrl;
58
+ this.mapboxOptions = options.mapbox;
59
+ this.receiveShadow = options.receiveShadow ?? true;
60
+ this.castShadow = options.castShadow ?? true;
61
+ this.useDemoData = options.useDemoData ?? false;
62
+ }
63
+ /**
64
+ * Update Mapbox imagery configuration at runtime
65
+ */
66
+ setMapboxOptions(options) {
67
+ this.mapboxOptions = options;
68
+ }
69
+ /**
70
+ * Loads terrain data and creates a mesh
71
+ * @param center - Center coordinates (latitude, longitude)
72
+ * @param dimensions - Terrain dimensions in meters
73
+ */
74
+ async loadTerrain(center, dimensions) {
75
+ if (this.isLoading) {
76
+ console.warn("Terrain is already loading");
77
+ return;
78
+ }
79
+ this.isLoading = true;
80
+ this.dispatchEvent({
81
+ type: "updateStarted",
82
+ center,
83
+ dimensions
84
+ });
85
+ try {
86
+ const terrainData = await this.fetchElevationData(center, dimensions);
87
+ this.currentData = terrainData;
88
+ this.dispatchEvent({
89
+ type: "dataLoaded",
90
+ data: terrainData
91
+ });
92
+ const textureSource = await this.resolveTexture(center, dimensions);
93
+ await this.createMesh(terrainData, textureSource);
94
+ } catch (error) {
95
+ console.error("Error loading terrain:", error);
96
+ this.dispatchEvent({
97
+ type: "error",
98
+ message: "Failed to load terrain",
99
+ error
100
+ });
101
+ } finally {
102
+ this.isLoading = false;
103
+ }
104
+ }
105
+ /**
106
+ * Updates the terrain with new coordinates and/or dimensions
107
+ * @param center - New center coordinates (optional, keeps current if not provided)
108
+ * @param dimensions - New dimensions (optional, keeps current if not provided)
109
+ */
110
+ async updateTerrain(center, dimensions) {
111
+ if (!this.currentData && !center) {
112
+ console.warn("No current terrain data and no center provided");
113
+ return;
114
+ }
115
+ const newCenter = center || this.currentData.center;
116
+ const newDimensions = dimensions || this.currentData.dimensions;
117
+ await this.loadTerrain(newCenter, newDimensions);
118
+ }
119
+ /**
120
+ * Fetches elevation data from Open-Elevation API or generates demo data
121
+ * @private
122
+ */
123
+ async fetchElevationData(center, dimensions) {
124
+ if (this.useDemoData) {
125
+ return this.generateDemoData(center, dimensions);
126
+ }
127
+ const points = [];
128
+ const metersPerDegreeLat = 111320;
129
+ const metersPerDegreeLon = 111320 * Math.max(Math.abs(Math.cos(center.latitude * Math.PI / 180)), 1e-6);
130
+ const latRange = dimensions.depth / metersPerDegreeLat;
131
+ const lonRange = dimensions.width / metersPerDegreeLon;
132
+ const latStep = latRange / this.depthSegments;
133
+ const lonStep = lonRange / this.widthSegments;
134
+ const startLat = center.latitude + latRange / 2;
135
+ const startLon = center.longitude - lonRange / 2;
136
+ for (let z = 0; z <= this.depthSegments; z++) {
137
+ for (let x = 0; x <= this.widthSegments; x++) {
138
+ points.push({
139
+ latitude: startLat - z * latStep,
140
+ longitude: startLon + x * lonStep
141
+ });
142
+ }
143
+ }
144
+ const response = await fetch(
145
+ "https://api.open-elevation.com/api/v1/lookup",
146
+ {
147
+ method: "POST",
148
+ headers: {
149
+ "Content-Type": "application/json"
150
+ },
151
+ body: JSON.stringify({
152
+ locations: points
153
+ })
154
+ }
155
+ );
156
+ if (!response.ok) {
157
+ throw new Error(`Elevation API error: ${response.statusText}`);
158
+ }
159
+ const data = await response.json();
160
+ const results = data.results;
161
+ const elevations = [];
162
+ let minElevation = Infinity;
163
+ let maxElevation = -Infinity;
164
+ for (let z = 0; z <= this.depthSegments; z++) {
165
+ const row = [];
166
+ for (let x = 0; x <= this.widthSegments; x++) {
167
+ const index = z * (this.widthSegments + 1) + x;
168
+ const elevation = results[index].elevation;
169
+ row.push(elevation);
170
+ minElevation = Math.min(minElevation, elevation);
171
+ maxElevation = Math.max(maxElevation, elevation);
172
+ }
173
+ elevations.push(row);
174
+ }
175
+ return {
176
+ center,
177
+ dimensions,
178
+ elevations,
179
+ minElevation,
180
+ maxElevation
181
+ };
182
+ }
183
+ /**
184
+ * Generates demo terrain data using perlin-like noise
185
+ * @private
186
+ */
187
+ generateDemoData(center, dimensions) {
188
+ const elevations = [];
189
+ let minElevation = Infinity;
190
+ let maxElevation = -Infinity;
191
+ const seed = center.latitude + center.longitude;
192
+ for (let z = 0; z <= this.depthSegments; z++) {
193
+ const row = [];
194
+ for (let x = 0; x <= this.widthSegments; x++) {
195
+ const nx = x / this.widthSegments * 4;
196
+ const nz = z / this.depthSegments * 4;
197
+ let elevation = 0;
198
+ elevation += Math.sin(nx + seed) * 500;
199
+ elevation += Math.cos(nz + seed) * 500;
200
+ elevation += Math.sin(nx * 2 + seed) * 200;
201
+ elevation += Math.cos(nz * 2 + seed) * 200;
202
+ elevation += Math.sin(nx * 4 + seed) * 50;
203
+ elevation += Math.cos(nz * 4 + seed) * 50;
204
+ elevation += (Math.random() - 0.5) * 30;
205
+ row.push(elevation);
206
+ minElevation = Math.min(minElevation, elevation);
207
+ maxElevation = Math.max(maxElevation, elevation);
208
+ }
209
+ elevations.push(row);
210
+ }
211
+ return {
212
+ center,
213
+ dimensions,
214
+ elevations,
215
+ minElevation,
216
+ maxElevation
217
+ };
218
+ }
219
+ /**
220
+ * Creates a terrain mesh from elevation data
221
+ * @private
222
+ */
223
+ async createMesh(data, textureSource) {
224
+ if (this.mesh) {
225
+ this.scene.remove(this.mesh);
226
+ this.mesh.geometry.dispose();
227
+ if (Array.isArray(this.mesh.material)) {
228
+ this.mesh.material.forEach((mat) => mat.dispose());
229
+ } else {
230
+ this.mesh.material.dispose();
231
+ }
232
+ }
233
+ const geometry = new THREE.PlaneGeometry(
234
+ data.dimensions.width,
235
+ data.dimensions.depth,
236
+ this.widthSegments,
237
+ this.depthSegments
238
+ );
239
+ const positions = geometry.attributes.position;
240
+ let vertexIndex = 0;
241
+ for (let z = 0; z <= this.depthSegments; z++) {
242
+ for (let x = 0; x <= this.widthSegments; x++) {
243
+ const elevation = data.elevations[z][x];
244
+ const height = (elevation - data.minElevation) * this.elevationScale;
245
+ positions.setZ(vertexIndex, height);
246
+ vertexIndex++;
247
+ }
248
+ }
249
+ geometry.computeVertexNormals();
250
+ let material;
251
+ if (textureSource?.url) {
252
+ try {
253
+ const texture = await new THREE.TextureLoader().loadAsync(
254
+ textureSource.url
255
+ );
256
+ material = new THREE.MeshStandardMaterial({
257
+ map: texture,
258
+ wireframe: this.wireframe
259
+ });
260
+ } catch (error) {
261
+ console.warn("Failed to load terrain texture, using base color.", error);
262
+ material = new THREE.MeshStandardMaterial({
263
+ color: this.baseColor,
264
+ wireframe: this.wireframe
265
+ });
266
+ } finally {
267
+ if (textureSource.revokeOnUse) {
268
+ this.releaseGeneratedTexture(textureSource.url);
269
+ }
270
+ }
271
+ } else {
272
+ material = new THREE.MeshStandardMaterial({
273
+ color: this.baseColor,
274
+ wireframe: this.wireframe
275
+ });
276
+ }
277
+ this.mesh = new THREE.Mesh(geometry, material);
278
+ this.mesh.rotation.x = -Math.PI / 2;
279
+ this.mesh.receiveShadow = this.receiveShadow;
280
+ this.mesh.castShadow = this.castShadow;
281
+ this.scene.add(this.mesh);
282
+ this.dispatchEvent({
283
+ type: "meshLoaded",
284
+ mesh: this.mesh
285
+ });
286
+ }
287
+ async resolveTexture(center, dimensions) {
288
+ if (this.textureUrl) {
289
+ return {
290
+ url: this.textureUrl,
291
+ revokeOnUse: false
292
+ };
293
+ }
294
+ if (!this.mapboxOptions) {
295
+ return void 0;
296
+ }
297
+ try {
298
+ const url = await this.fetchMapboxTexture(
299
+ center,
300
+ dimensions,
301
+ this.mapboxOptions
302
+ );
303
+ this.generatedTextureUrls.add(url);
304
+ return {
305
+ url,
306
+ revokeOnUse: true
307
+ };
308
+ } catch (error) {
309
+ console.warn(
310
+ "Failed to fetch Mapbox imagery, falling back to base material.",
311
+ error
312
+ );
313
+ return void 0;
314
+ }
315
+ }
316
+ computeBoundingBox(center, dimensions, paddingRatio) {
317
+ const metersPerDegreeLat = 111320;
318
+ const latRadians = center.latitude * Math.PI / 180;
319
+ const cosLat = Math.cos(latRadians);
320
+ const metersPerDegreeLon = 111320 * Math.max(Math.abs(cosLat), 1e-6);
321
+ const appliedPadding = Math.max(0, paddingRatio);
322
+ const widthWithPadding = dimensions.width * (1 + appliedPadding);
323
+ const depthWithPadding = dimensions.depth * (1 + appliedPadding);
324
+ const halfDepthDegrees = depthWithPadding / 2 / metersPerDegreeLat;
325
+ const halfWidthDegrees = widthWithPadding / 2 / metersPerDegreeLon;
326
+ const clamp = (value, min, max) => Math.min(Math.max(value, min), max);
327
+ const minLat = clamp(center.latitude - halfDepthDegrees, -90, 90);
328
+ const maxLat = clamp(center.latitude + halfDepthDegrees, -90, 90);
329
+ const minLon = clamp(center.longitude - halfWidthDegrees, -180, 180);
330
+ const maxLon = clamp(center.longitude + halfWidthDegrees, -180, 180);
331
+ return { minLat, maxLat, minLon, maxLon };
332
+ }
333
+ async fetchMapboxTexture(center, dimensions, options) {
334
+ const styleId = options.styleId ?? "mapbox/satellite-v9";
335
+ const normalizedStyleId = styleId.replace("mapbox://styles/", "").replace(/^\/+/, "");
336
+ const width = Math.min(
337
+ 1280,
338
+ Math.max(1, Math.floor(options.imageWidth ?? 1024))
339
+ );
340
+ const height = Math.min(
341
+ 1280,
342
+ Math.max(1, Math.floor(options.imageHeight ?? 1024))
343
+ );
344
+ const highResSuffix = options.highResolution ? "@2x" : "";
345
+ const format = options.imageFormat ?? "png";
346
+ const paddingRatio = options.paddingRatio ?? 0.1;
347
+ const bounds = this.computeBoundingBox(center, dimensions, paddingRatio);
348
+ const bbox = `${bounds.minLon.toFixed(6)},${bounds.minLat.toFixed(
349
+ 6
350
+ )},${bounds.maxLon.toFixed(6)},${bounds.maxLat.toFixed(6)}`;
351
+ const params = new URLSearchParams({
352
+ access_token: options.accessToken,
353
+ format
354
+ });
355
+ const requestUrl = `https://api.mapbox.com/styles/v1/${normalizedStyleId}/static/[${bbox}]/${width}x${height}${highResSuffix}?${params.toString()}`;
356
+ const response = await fetch(requestUrl);
357
+ if (!response.ok) {
358
+ throw new Error(
359
+ `Mapbox imagery request failed: ${response.status} ${response.statusText}`
360
+ );
361
+ }
362
+ const blob = await response.blob();
363
+ if (typeof URL === "undefined" || typeof URL.createObjectURL !== "function") {
364
+ throw new Error(
365
+ "URL.createObjectURL is not available in this environment"
366
+ );
367
+ }
368
+ return URL.createObjectURL(blob);
369
+ }
370
+ releaseGeneratedTexture(url) {
371
+ if (!this.generatedTextureUrls.has(url)) {
372
+ return;
373
+ }
374
+ if (typeof URL !== "undefined" && typeof URL.revokeObjectURL === "function") {
375
+ URL.revokeObjectURL(url);
376
+ }
377
+ this.generatedTextureUrls.delete(url);
378
+ }
379
+ /**
380
+ * Gets the current terrain mesh
381
+ */
382
+ getMesh() {
383
+ return this.mesh;
384
+ }
385
+ /**
386
+ * Gets the current terrain data
387
+ */
388
+ getData() {
389
+ return this.currentData;
390
+ }
391
+ /**
392
+ * Checks if terrain is currently loading
393
+ */
394
+ isTerrainLoading() {
395
+ return this.isLoading;
396
+ }
397
+ /**
398
+ * Disposes of all resources
399
+ */
400
+ dispose() {
401
+ if (this.mesh) {
402
+ this.scene.remove(this.mesh);
403
+ this.mesh.geometry.dispose();
404
+ if (Array.isArray(this.mesh.material)) {
405
+ this.mesh.material.forEach((mat) => mat.dispose());
406
+ } else {
407
+ this.mesh.material.dispose();
408
+ }
409
+ this.mesh = null;
410
+ }
411
+ this.currentData = null;
412
+ if (this.generatedTextureUrls.size > 0) {
413
+ for (const url of Array.from(this.generatedTextureUrls)) {
414
+ this.releaseGeneratedTexture(url);
415
+ }
416
+ }
417
+ }
418
+ };
419
+ // Annotate the CommonJS export names for ESM import in node:
420
+ 0 && (module.exports = {
421
+ TerrainTool
422
+ });
423
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../packages/terrain/src/index.ts","../../packages/terrain/src/TerrainTool.ts"],"sourcesContent":["export { TerrainTool } from './TerrainTool'\nexport type {\n GeoCoordinates,\n TerrainDimensions,\n TerrainToolOptions,\n TerrainData,\n TerrainToolEvents,\n ElevationPoint,\n} from './TerrainTypes'\n","import * as THREE from 'three'\nimport {\n GeoCoordinates,\n TerrainDimensions,\n TerrainToolOptions,\n TerrainData,\n TerrainToolEvents,\n ElevationPoint,\n MapboxTextureOptions,\n} from './TerrainTypes'\n\ntype TerrainTextureSource = {\n url: string\n revokeOnUse: boolean\n}\n\n/**\n * Terrain mesh creator for Three.js scenes\n * Fetches elevation data from Open-Elevation API and creates 3D terrain meshes\n *\n * @example\n * ```javascript\n * const terrainTool = new TerrainTool(scene, {\n * widthSegments: 100,\n * depthSegments: 100,\n * elevationScale: 2.0,\n * useDemoData: false // Set to true for demo/testing without API\n * })\n *\n * // Listen for events\n * terrainTool.addEventListener('dataLoaded', (e) => {\n * console.log('Terrain data loaded:', e.data)\n * })\n *\n * terrainTool.addEventListener('meshLoaded', (e) => {\n * console.log('Terrain mesh created:', e.mesh)\n * })\n *\n * // Load terrain for a location\n * terrainTool.loadTerrain(\n * { latitude: 36.1699, longitude: -115.1398 }, // Las Vegas\n * { width: 5000, depth: 5000 } // 5km x 5km\n * )\n * ```\n */\nexport class TerrainTool extends THREE.EventDispatcher<TerrainToolEvents> {\n private scene: THREE.Scene\n private mesh: THREE.Mesh | null = null\n private currentData: TerrainData | null = null\n private isLoading: boolean = false\n private mapboxOptions?: MapboxTextureOptions\n private generatedTextureUrls: Set<string> = new Set()\n\n // Configuration\n public widthSegments: number\n public depthSegments: number\n public elevationScale: number\n public baseColor: number\n public wireframe: boolean\n public textureUrl: string | undefined\n public receiveShadow: boolean\n public castShadow: boolean\n public useDemoData: boolean\n\n /**\n * Creates a new TerrainTool instance\n * @param scene - The Three.js scene to add terrain to\n * @param options - Configuration options\n */\n constructor(scene: THREE.Scene, options: TerrainToolOptions = {}) {\n super()\n this.scene = scene\n\n // Apply options with defaults\n this.widthSegments = options.widthSegments ?? 50\n this.depthSegments = options.depthSegments ?? 50\n this.elevationScale = options.elevationScale ?? 1.0\n this.baseColor = options.baseColor ?? 0x8b7355\n this.wireframe = options.wireframe ?? false\n this.textureUrl = options.textureUrl\n this.mapboxOptions = options.mapbox\n this.receiveShadow = options.receiveShadow ?? true\n this.castShadow = options.castShadow ?? true\n this.useDemoData = options.useDemoData ?? false\n }\n\n /**\n * Update Mapbox imagery configuration at runtime\n */\n setMapboxOptions(options?: MapboxTextureOptions): void {\n this.mapboxOptions = options\n }\n\n /**\n * Loads terrain data and creates a mesh\n * @param center - Center coordinates (latitude, longitude)\n * @param dimensions - Terrain dimensions in meters\n */\n async loadTerrain(\n center: GeoCoordinates,\n dimensions: TerrainDimensions\n ): Promise<void> {\n if (this.isLoading) {\n console.warn('Terrain is already loading')\n return\n }\n\n this.isLoading = true\n this.dispatchEvent({\n type: 'updateStarted',\n center,\n dimensions,\n })\n\n try {\n // Fetch elevation data\n const terrainData = await this.fetchElevationData(center, dimensions)\n\n // Store the data\n this.currentData = terrainData\n\n // Dispatch data loaded event\n this.dispatchEvent({\n type: 'dataLoaded',\n data: terrainData,\n })\n\n const textureSource = await this.resolveTexture(center, dimensions)\n\n // Create and add mesh to scene\n await this.createMesh(terrainData, textureSource)\n } catch (error) {\n console.error('Error loading terrain:', error)\n this.dispatchEvent({\n type: 'error',\n message: 'Failed to load terrain',\n error: error as Error,\n })\n } finally {\n this.isLoading = false\n }\n }\n\n /**\n * Updates the terrain with new coordinates and/or dimensions\n * @param center - New center coordinates (optional, keeps current if not provided)\n * @param dimensions - New dimensions (optional, keeps current if not provided)\n */\n async updateTerrain(\n center?: GeoCoordinates,\n dimensions?: TerrainDimensions\n ): Promise<void> {\n if (!this.currentData && !center) {\n console.warn('No current terrain data and no center provided')\n return\n }\n\n const newCenter = center || this.currentData!.center\n const newDimensions = dimensions || this.currentData!.dimensions\n\n await this.loadTerrain(newCenter, newDimensions)\n }\n\n /**\n * Fetches elevation data from Open-Elevation API or generates demo data\n * @private\n */\n private async fetchElevationData(\n center: GeoCoordinates,\n dimensions: TerrainDimensions\n ): Promise<TerrainData> {\n if (this.useDemoData) {\n return this.generateDemoData(center, dimensions)\n }\n\n // Calculate grid of lat/lon points\n const points: { latitude: number; longitude: number }[] = []\n\n // Approximate degrees per meter at this latitude\n const metersPerDegreeLat = 111320\n const metersPerDegreeLon =\n 111320 *\n Math.max(Math.abs(Math.cos((center.latitude * Math.PI) / 180)), 1e-6)\n\n const latRange = dimensions.depth / metersPerDegreeLat\n const lonRange = dimensions.width / metersPerDegreeLon\n\n const latStep = latRange / this.depthSegments\n const lonStep = lonRange / this.widthSegments\n\n const startLat = center.latitude + latRange / 2\n const startLon = center.longitude - lonRange / 2\n\n // Create grid of points\n for (let z = 0; z <= this.depthSegments; z++) {\n for (let x = 0; x <= this.widthSegments; x++) {\n points.push({\n latitude: startLat - z * latStep,\n longitude: startLon + x * lonStep,\n })\n }\n }\n\n // Fetch elevation data from Open-Elevation API\n // Note: This API has rate limits and may not work for large requests\n // In production, you might want to use a different API or cache results\n const response = await fetch(\n 'https://api.open-elevation.com/api/v1/lookup',\n {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n locations: points,\n }),\n }\n )\n\n if (!response.ok) {\n throw new Error(`Elevation API error: ${response.statusText}`)\n }\n\n const data = await response.json()\n const results: ElevationPoint[] = data.results\n\n // Convert to 2D grid\n const elevations: number[][] = []\n let minElevation = Infinity\n let maxElevation = -Infinity\n\n for (let z = 0; z <= this.depthSegments; z++) {\n const row: number[] = []\n for (let x = 0; x <= this.widthSegments; x++) {\n const index = z * (this.widthSegments + 1) + x\n const elevation = results[index].elevation\n row.push(elevation)\n minElevation = Math.min(minElevation, elevation)\n maxElevation = Math.max(maxElevation, elevation)\n }\n elevations.push(row)\n }\n\n return {\n center,\n dimensions,\n elevations,\n minElevation,\n maxElevation,\n }\n }\n\n /**\n * Generates demo terrain data using perlin-like noise\n * @private\n */\n private generateDemoData(\n center: GeoCoordinates,\n dimensions: TerrainDimensions\n ): TerrainData {\n const elevations: number[][] = []\n let minElevation = Infinity\n let maxElevation = -Infinity\n\n // Simple sine-based terrain generation for demo\n const seed = center.latitude + center.longitude\n for (let z = 0; z <= this.depthSegments; z++) {\n const row: number[] = []\n for (let x = 0; x <= this.widthSegments; x++) {\n // Create varied terrain using multiple sine waves\n const nx = (x / this.widthSegments) * 4\n const nz = (z / this.depthSegments) * 4\n\n let elevation = 0\n // Large features\n elevation += Math.sin(nx + seed) * 500\n elevation += Math.cos(nz + seed) * 500\n // Medium features\n elevation += Math.sin(nx * 2 + seed) * 200\n elevation += Math.cos(nz * 2 + seed) * 200\n // Small features\n elevation += Math.sin(nx * 4 + seed) * 50\n elevation += Math.cos(nz * 4 + seed) * 50\n // Random noise\n elevation += (Math.random() - 0.5) * 30\n\n row.push(elevation)\n minElevation = Math.min(minElevation, elevation)\n maxElevation = Math.max(maxElevation, elevation)\n }\n elevations.push(row)\n }\n\n return {\n center,\n dimensions,\n elevations,\n minElevation,\n maxElevation,\n }\n }\n\n /**\n * Creates a terrain mesh from elevation data\n * @private\n */\n private async createMesh(\n data: TerrainData,\n textureSource?: TerrainTextureSource\n ): Promise<void> {\n // Remove existing mesh if any\n if (this.mesh) {\n this.scene.remove(this.mesh)\n this.mesh.geometry.dispose()\n if (Array.isArray(this.mesh.material)) {\n this.mesh.material.forEach((mat) => mat.dispose())\n } else {\n this.mesh.material.dispose()\n }\n }\n\n // Create plane geometry\n const geometry = new THREE.PlaneGeometry(\n data.dimensions.width,\n data.dimensions.depth,\n this.widthSegments,\n this.depthSegments\n )\n\n // Apply elevation data to geometry vertices\n const positions = geometry.attributes.position\n let vertexIndex = 0\n\n for (let z = 0; z <= this.depthSegments; z++) {\n for (let x = 0; x <= this.widthSegments; x++) {\n const elevation = data.elevations[z][x]\n // Normalize elevation relative to min elevation\n const height = (elevation - data.minElevation) * this.elevationScale\n positions.setZ(vertexIndex, height)\n vertexIndex++\n }\n }\n\n // Recompute normals for proper lighting\n geometry.computeVertexNormals()\n\n // Create material\n let material: THREE.Material\n\n if (textureSource?.url) {\n try {\n const texture = await new THREE.TextureLoader().loadAsync(\n textureSource.url\n )\n material = new THREE.MeshStandardMaterial({\n map: texture,\n wireframe: this.wireframe,\n })\n } catch (error) {\n console.warn('Failed to load terrain texture, using base color.', error)\n material = new THREE.MeshStandardMaterial({\n color: this.baseColor,\n wireframe: this.wireframe,\n })\n } finally {\n if (textureSource.revokeOnUse) {\n this.releaseGeneratedTexture(textureSource.url)\n }\n }\n } else {\n material = new THREE.MeshStandardMaterial({\n color: this.baseColor,\n wireframe: this.wireframe,\n })\n }\n\n // Create mesh\n this.mesh = new THREE.Mesh(geometry, material)\n this.mesh.rotation.x = -Math.PI / 2 // Rotate to horizontal\n this.mesh.receiveShadow = this.receiveShadow\n this.mesh.castShadow = this.castShadow\n\n // Add to scene\n this.scene.add(this.mesh)\n\n // Dispatch mesh loaded event\n this.dispatchEvent({\n type: 'meshLoaded',\n mesh: this.mesh,\n })\n }\n\n private async resolveTexture(\n center: GeoCoordinates,\n dimensions: TerrainDimensions\n ): Promise<TerrainTextureSource | undefined> {\n if (this.textureUrl) {\n return {\n url: this.textureUrl,\n revokeOnUse: false,\n }\n }\n\n if (!this.mapboxOptions) {\n return undefined\n }\n\n try {\n const url = await this.fetchMapboxTexture(\n center,\n dimensions,\n this.mapboxOptions\n )\n this.generatedTextureUrls.add(url)\n return {\n url,\n revokeOnUse: true,\n }\n } catch (error) {\n console.warn(\n 'Failed to fetch Mapbox imagery, falling back to base material.',\n error\n )\n return undefined\n }\n }\n\n private computeBoundingBox(\n center: GeoCoordinates,\n dimensions: TerrainDimensions,\n paddingRatio: number\n ): { minLat: number; maxLat: number; minLon: number; maxLon: number } {\n const metersPerDegreeLat = 111320\n const latRadians = (center.latitude * Math.PI) / 180\n const cosLat = Math.cos(latRadians)\n const metersPerDegreeLon = 111320 * Math.max(Math.abs(cosLat), 1e-6)\n\n const appliedPadding = Math.max(0, paddingRatio)\n const widthWithPadding = dimensions.width * (1 + appliedPadding)\n const depthWithPadding = dimensions.depth * (1 + appliedPadding)\n\n const halfDepthDegrees = depthWithPadding / 2 / metersPerDegreeLat\n const halfWidthDegrees = widthWithPadding / 2 / metersPerDegreeLon\n\n const clamp = (value: number, min: number, max: number) =>\n Math.min(Math.max(value, min), max)\n\n const minLat = clamp(center.latitude - halfDepthDegrees, -90, 90)\n const maxLat = clamp(center.latitude + halfDepthDegrees, -90, 90)\n const minLon = clamp(center.longitude - halfWidthDegrees, -180, 180)\n const maxLon = clamp(center.longitude + halfWidthDegrees, -180, 180)\n\n return { minLat, maxLat, minLon, maxLon }\n }\n\n private async fetchMapboxTexture(\n center: GeoCoordinates,\n dimensions: TerrainDimensions,\n options: MapboxTextureOptions\n ): Promise<string> {\n const styleId = options.styleId ?? 'mapbox/satellite-v9'\n const normalizedStyleId = styleId\n .replace('mapbox://styles/', '')\n .replace(/^\\/+/, '')\n\n const width = Math.min(\n 1280,\n Math.max(1, Math.floor(options.imageWidth ?? 1024))\n )\n const height = Math.min(\n 1280,\n Math.max(1, Math.floor(options.imageHeight ?? 1024))\n )\n const highResSuffix = options.highResolution ? '@2x' : ''\n const format = options.imageFormat ?? 'png'\n const paddingRatio = options.paddingRatio ?? 0.1\n\n const bounds = this.computeBoundingBox(center, dimensions, paddingRatio)\n const bbox = `${bounds.minLon.toFixed(6)},${bounds.minLat.toFixed(\n 6\n )},${bounds.maxLon.toFixed(6)},${bounds.maxLat.toFixed(6)}`\n\n const params = new URLSearchParams({\n access_token: options.accessToken,\n format,\n })\n\n const requestUrl = `https://api.mapbox.com/styles/v1/${normalizedStyleId}/static/[${bbox}]/${width}x${height}${highResSuffix}?${params.toString()}`\n\n const response = await fetch(requestUrl)\n if (!response.ok) {\n throw new Error(\n `Mapbox imagery request failed: ${response.status} ${response.statusText}`\n )\n }\n\n const blob = await response.blob()\n\n if (\n typeof URL === 'undefined' ||\n typeof URL.createObjectURL !== 'function'\n ) {\n throw new Error(\n 'URL.createObjectURL is not available in this environment'\n )\n }\n\n return URL.createObjectURL(blob)\n }\n\n private releaseGeneratedTexture(url: string): void {\n if (!this.generatedTextureUrls.has(url)) {\n return\n }\n\n if (\n typeof URL !== 'undefined' &&\n typeof URL.revokeObjectURL === 'function'\n ) {\n URL.revokeObjectURL(url)\n }\n\n this.generatedTextureUrls.delete(url)\n }\n\n /**\n * Gets the current terrain mesh\n */\n getMesh(): THREE.Mesh | null {\n return this.mesh\n }\n\n /**\n * Gets the current terrain data\n */\n getData(): TerrainData | null {\n return this.currentData\n }\n\n /**\n * Checks if terrain is currently loading\n */\n isTerrainLoading(): boolean {\n return this.isLoading\n }\n\n /**\n * Disposes of all resources\n */\n dispose(): void {\n if (this.mesh) {\n this.scene.remove(this.mesh)\n this.mesh.geometry.dispose()\n if (Array.isArray(this.mesh.material)) {\n this.mesh.material.forEach((mat) => mat.dispose())\n } else {\n this.mesh.material.dispose()\n }\n this.mesh = null\n }\n this.currentData = null\n\n if (this.generatedTextureUrls.size > 0) {\n for (const url of Array.from(this.generatedTextureUrls)) {\n this.releaseGeneratedTexture(url)\n }\n }\n }\n}\n"],"mappings":"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","names":[]}