@pirireis/webglobeplugins 0.17.0 → 0.17.1

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.
package/Math/methods.js CHANGED
@@ -16,6 +16,12 @@ export const radianToMercator = (xy) => {
16
16
  WORLD_RADIUS_MERCATOR * Math.log(Math.tan(Math.PI / 4 + xy[1] / 2))
17
17
  ];
18
18
  };
19
+ export function radianToMercatorXY(xy) {
20
+ return [
21
+ xy[0] * Math.pow(2, 8),
22
+ Math.log(Math.tan(Math.PI / 4 + xy[1] / 2)) * Math.pow(2, 8)
23
+ ];
24
+ }
19
25
  export const radianToCartesian3d = (xy) => {
20
26
  const x = Math.cos(xy[1]) * Math.cos(xy[0]);
21
27
  const y = Math.cos(xy[1]) * Math.sin(xy[0]);
@@ -1,56 +1,298 @@
1
- "use strict";
2
- function _sortXY(a, b) {
3
- if (a.y === b.y) {
4
- return a.x - b.x;
5
- }
6
- return a.y - b.y;
1
+ function displayTileMetaData(tilesMetaData) {
2
+ // show maxZoom minZoom
3
+ console.log(`Max Level: ${tilesMetaData.maxLevel}
4
+ Min Level: ${tilesMetaData.minLevel}`);
5
+ }
6
+ function keyMethod(row, column, level) {
7
+ return `${level}_${row}_${column}`;
7
8
  }
8
- function groupByZoomLevel(tiles) {
9
- const map = new Map();
9
+ function calculateParentTile(row, column, level) {
10
+ return {
11
+ row: Math.floor(row / 2),
12
+ column: Math.floor(column / 2),
13
+ level: level - 1
14
+ };
15
+ }
16
+ function createMapAndMetaData(tiles) {
17
+ const tileMap = new Map();
18
+ let maxLevel = -Infinity;
19
+ let minLevel = Infinity;
20
+ const tileLimitsByZoom = new Map();
10
21
  for (const tile of tiles) {
11
- if (!map.has(tile.z)) {
12
- map.set(tile.z, []);
22
+ const key = keyMethod(tile.row, tile.column, tile.level);
23
+ tileMap.set(key, tile);
24
+ if (tile.level > maxLevel) {
25
+ maxLevel = tile.level;
26
+ }
27
+ if (tile.level < minLevel) {
28
+ minLevel = tile.level;
29
+ }
30
+ if (!tileLimitsByZoom.has(tile.level)) {
31
+ tileLimitsByZoom.set(tile.level, {
32
+ minRow: tile.row,
33
+ maxRow: tile.row,
34
+ minCol: tile.column,
35
+ maxCol: tile.column
36
+ });
37
+ }
38
+ else {
39
+ const limits = tileLimitsByZoom.get(tile.level);
40
+ if (tile.row < limits.minRow)
41
+ limits.minRow = tile.row;
42
+ if (tile.row > limits.maxRow)
43
+ limits.maxRow = tile.row;
44
+ if (tile.column < limits.minCol)
45
+ limits.minCol = tile.column;
46
+ if (tile.column > limits.maxCol)
47
+ limits.maxCol = tile.column;
13
48
  }
14
- map.get(tile.z).push(tile);
15
49
  }
16
- map.forEach((tileList, z) => {
17
- tileList.sort(_sortXY);
18
- });
19
- return map;
20
- }
21
- function _insertDEM(sourceDEM, sourceWidth, sourceHeight, targetDEM, targetWidth, targetHeight, offsetX, offsetY) {
22
- for (let row = 0; row < sourceHeight; row++) {
23
- for (let col = 0; col < sourceWidth; col++) {
24
- const sourceIndex = row * sourceWidth + col;
25
- const targetIndex = (row + offsetY) * targetWidth + (col + offsetX);
26
- targetDEM[targetIndex] = sourceDEM[sourceIndex];
50
+ return {
51
+ tileMap,
52
+ maxLevel,
53
+ minLevel,
54
+ tileLimitsByZoom
55
+ };
56
+ }
57
+ function populateTileIntoHigherZoomLevel(tileMesh, tileDimensionLength, targetDimensionLength) {
58
+ const newMesh = new Array(targetDimensionLength).fill(0).map(() => new Float32Array(targetDimensionLength));
59
+ // use linear interpolation to populate
60
+ for (let row = 0; row < targetDimensionLength; row++) {
61
+ for (let col = 0; col < targetDimensionLength; col++) {
62
+ const srcRow = row * (tileDimensionLength - 1) / (targetDimensionLength - 1);
63
+ const srcCol = col * (tileDimensionLength - 1) / (targetDimensionLength - 1);
64
+ const srcRowLow = Math.floor(srcRow);
65
+ const srcRowHigh = Math.min(Math.ceil(srcRow), tileDimensionLength - 1);
66
+ const srcColLow = Math.floor(srcCol);
67
+ const srcColHigh = Math.min(Math.ceil(srcCol), tileDimensionLength - 1);
68
+ const topLeft = tileMesh[srcRowLow][srcColLow];
69
+ const topRight = tileMesh[srcRowLow][srcColHigh];
70
+ const bottomLeft = tileMesh[srcRowHigh][srcColLow];
71
+ const bottomRight = tileMesh[srcRowHigh][srcColHigh];
72
+ const top = topLeft + (topRight - topLeft) * (srcCol - srcColLow);
73
+ const bottom = bottomLeft + (bottomRight - bottomLeft) * (srcCol - srcColLow);
74
+ newMesh[row][col] = top + (bottom - top) * (srcRow - srcRowLow);
27
75
  }
28
76
  }
77
+ return newMesh;
29
78
  }
30
- function mergeTiles(tiles, demWidth, demHeight) {
31
- let minX = Number.MAX_VALUE;
32
- let maxX = Number.MIN_VALUE;
33
- let minY = Number.MAX_VALUE;
34
- let maxY = Number.MIN_VALUE;
35
- for (let tile of tiles) {
36
- if (tile.x < minX) {
37
- minX = tile.x;
79
+ // This function is correct, assuming its inputs are prepared properly.
80
+ // This function is correct, assuming its inputs are prepared properly.
81
+ function moveMeshToMergedMesh(targetMesh, targetDimensionLength, sourceMesh, sourceDimensionLength, startX, startY) {
82
+ for (let row = 0; row < sourceDimensionLength; row++) {
83
+ for (let col = 0; col < sourceDimensionLength; col++) {
84
+ const targetIndex = (startY + row) * targetDimensionLength + (startX + col);
85
+ // Check if source value exists
86
+ if (sourceMesh[row] === undefined || sourceMesh[row][col] === undefined) {
87
+ console.warn(`Source data missing at [${row}][${col}]`);
88
+ continue;
89
+ }
90
+ const value = sourceMesh[row][col];
91
+ if (isNaN(value)) {
92
+ console.warn(`NaN value found in source mesh at row: ${row}, col: ${col}`);
93
+ }
94
+ ;
95
+ if (targetIndex >= targetMesh.length) {
96
+ console.warn(`⚠️ Target index out of bounds: ${targetIndex} >= ${targetMesh.length}`);
97
+ continue;
98
+ }
99
+ targetMesh[targetIndex] = value;
38
100
  }
39
- if (tile.x > maxX) {
40
- maxX = tile.x;
101
+ }
102
+ }
103
+ function limitCheck(limit, limitRange = 12) {
104
+ const rowRange = limit.maxRow - limit.minRow + 1;
105
+ const colRange = limit.maxCol - limit.minCol + 1;
106
+ if (rowRange > limitRange || colRange > limitRange) {
107
+ console.warn(`Tile limit range exceeded: Row Range = ${rowRange}, Col Range = ${colRange}, Limit Range = ${limitRange}`);
108
+ throw new Error("Tile limit range exceeded");
109
+ }
110
+ }
111
+ function mergeTiles(tilesMetaData, mergeCount = 8, tileDimensionLength = 5, processLimit = 6) {
112
+ const mergedMeshDimLength = tileDimensionLength * mergeCount - (mergeCount - 1);
113
+ const processedTiles = new Set();
114
+ const result = [];
115
+ for (let zoom = tilesMetaData.maxLevel; zoom >= tilesMetaData.minLevel; zoom--) {
116
+ let processedTileCountFromCurrentLevel = 0;
117
+ let processedTileCountFromParentLevel = 0;
118
+ if (tilesMetaData.maxLevel - zoom >= processLimit) {
119
+ break;
41
120
  }
42
- if (tile.y < minY) {
43
- minY = tile.y;
121
+ const limits = tilesMetaData.tileLimitsByZoom.get(zoom);
122
+ const mergedMesh = new Float32Array(mergedMeshDimLength * mergedMeshDimLength).fill(0);
123
+ const bboxLimit = {
124
+ ur: { x: -Infinity, y: -Infinity },
125
+ ll: { x: Infinity, y: Infinity }
126
+ };
127
+ if (!limits)
128
+ continue;
129
+ limitCheck(limits, 12);
130
+ for (let row = limits.minRow; row <= limits.maxRow; row++) {
131
+ for (let col = limits.minCol; col <= limits.maxCol; col++) {
132
+ const key = keyMethod(row, col, zoom);
133
+ if (processedTiles.has(key)) {
134
+ continue;
135
+ }
136
+ const tile = tilesMetaData.tileMap.get(key);
137
+ if (tile) {
138
+ // move tile mesh to merged mesh
139
+ moveMeshToMergedMesh(mergedMesh, mergedMeshDimLength, tile.mesh, tileDimensionLength, (row - limits.minRow) * (tileDimensionLength - 1), (col - limits.minCol) * (tileDimensionLength - 1));
140
+ // moveMeshToMergedMesh2(
141
+ // mergedMesh, mergedMeshDimLength,
142
+ // (row + col) % 2 === 0 ? 0.1 : 0,
143
+ // tileDimensionLength,
144
+ // (row - limits.minRow) * (tileDimensionLength - 1), (col - limits.minCol) * (tileDimensionLength - 1)
145
+ // );
146
+ processedTiles.add(key);
147
+ processedTileCountFromCurrentLevel++;
148
+ // update bbox
149
+ if (tile.bbox.ll.x < bboxLimit.ll.x)
150
+ bboxLimit.ll.x = tile.bbox.ll.x;
151
+ if (tile.bbox.ll.y < bboxLimit.ll.y)
152
+ bboxLimit.ll.y = tile.bbox.ll.y;
153
+ if (tile.bbox.ur.x > bboxLimit.ur.x)
154
+ bboxLimit.ur.x = tile.bbox.ur.x;
155
+ if (tile.bbox.ur.y > bboxLimit.ur.y)
156
+ bboxLimit.ur.y = tile.bbox.ur.y;
157
+ /**
158
+ } else {
159
+ // try to find parent tile
160
+ const parent = calculateParentTile(row, col, zoom);
161
+ const parentKey = keyMethod(parent.row, parent.column, parent.level);
162
+ const parentTile = tilesMetaData.tileMap.get(parentKey);
163
+
164
+ if (parentTile) {
165
+ // Parent's 4 children at current zoom level
166
+ const parentTopLeftChildRow = parent.row * 2;
167
+ const parentTopLeftChildCol = parent.column * 2;
168
+ const topLeftChildKey = keyMethod(parentTopLeftChildRow, parentTopLeftChildCol, zoom);
169
+
170
+ // Only process parent once
171
+ if (!processedTiles.has(topLeftChildKey)) {
172
+ // Check if parent's area is within or overlaps the current limits
173
+ const parentBottomRightChildRow = parentTopLeftChildRow + 1;
174
+ const parentBottomRightChildCol = parentTopLeftChildCol + 1;
175
+
176
+ // Check if parent's children overlap with current zoom's limits
177
+ if (parentTopLeftChildRow > limits.maxRow ||
178
+ parentBottomRightChildRow < limits.minRow ||
179
+ parentTopLeftChildCol > limits.maxCol ||
180
+ parentBottomRightChildCol < limits.minCol) {
181
+ // Parent doesn't overlap, skip
182
+ processedTiles.add(key);
183
+ continue;
184
+ }
185
+
186
+ // Upscale parent to cover all 4 children
187
+ const parentTileUpscaledMesh = populateTileIntoHigherZoomLevel(
188
+ parentTile.mesh,
189
+ tileDimensionLength,
190
+ tileDimensionLength * 2 - 1
191
+ );
192
+
193
+ // Calculate destination position (parent's top-left child position)
194
+ // Clamp to limits to handle edge cases
195
+ const actualStartRow = Math.max(parentTopLeftChildRow, limits.minRow);
196
+ const actualStartCol = Math.max(parentTopLeftChildCol, limits.minCol);
197
+
198
+ const destRow = (actualStartRow - limits.minRow) * (tileDimensionLength - 1);
199
+ const destCol = (actualStartCol - limits.minCol) * (tileDimensionLength - 1);
200
+
201
+ // Calculate source offset if parent starts before limits
202
+ const srcRowOffset = (actualStartRow - parentTopLeftChildRow) * (tileDimensionLength - 1);
203
+ const srcColOffset = (actualStartCol - parentTopLeftChildCol) * (tileDimensionLength - 1);
204
+
205
+ // Calculate how much to copy
206
+ const actualEndRow = Math.min(parentBottomRightChildRow, limits.maxRow);
207
+ const actualEndCol = Math.min(parentBottomRightChildCol, limits.maxCol);
208
+
209
+ const copyRows = (actualEndRow - actualStartRow + 1) * (tileDimensionLength - 1);
210
+ const copyCols = (actualEndCol - actualStartCol + 1) * (tileDimensionLength - 1);
211
+
212
+ // Copy only the visible portion
213
+ moveMeshToMergedMeshPartial(
214
+ mergedMesh,
215
+ mergedMeshDimLength,
216
+ parentTileUpscaledMesh,
217
+ tileDimensionLength * 2 - 1,
218
+ destRow,
219
+ destCol,
220
+ srcRowOffset,
221
+ srcColOffset,
222
+ copyRows,
223
+ copyCols
224
+ );
225
+
226
+ // Mark all 4 child positions as processed (even if out of bounds)
227
+ for (let dr = 0; dr < 2; dr++) {
228
+ for (let dc = 0; dc < 2; dc++) {
229
+ const childRow = parentTopLeftChildRow + dr;
230
+ const childCol = parentTopLeftChildCol + dc;
231
+ const childKey = keyMethod(childRow, childCol, zoom);
232
+ processedTiles.add(childKey);
233
+ }
234
+ }
235
+
236
+ processedTileCountFromParentLevel++;
237
+
238
+ // Update bbox (use actual parent bbox, not child bbox)
239
+ if (parentTile.bbox.ll.x < bboxLimit.ll.x) bboxLimit.ll.x = parentTile.bbox.ll.x;
240
+ if (parentTile.bbox.ll.y < bboxLimit.ll.y) bboxLimit.ll.y = parentTile.bbox.ll.y;
241
+ if (parentTile.bbox.ur.x > bboxLimit.ur.x) bboxLimit.ur.x = parentTile.bbox.ur.x;
242
+ if (parentTile.bbox.ur.y > bboxLimit.ur.y) bboxLimit.ur.y = parentTile.bbox.ur.y;
243
+ } else {
244
+ // Already processed via parent
245
+ processedTiles.add(key);
246
+ }
247
+ } else {
248
+ // No parent available
249
+ processedTiles.add(key);
250
+ }
251
+
252
+ */
253
+ }
254
+ }
44
255
  }
45
- if (tile.y > maxY) {
46
- maxY = tile.y;
256
+ const rowRatio = (limits.maxRow - limits.minRow + 1) / mergeCount;
257
+ const colRatio = (limits.maxCol - limits.minCol + 1) / mergeCount;
258
+ // console.log(`Zoom ${zoom}: Processed ${processedTileCountFromCurrentLevel} from current level, ${processedTileCountFromParentLevel} from parent level. Coverage: ${(rowRatio * 100).toFixed(1)}% x ${(colRatio * 100).toFixed(1)}%`);
259
+ result.push({ mesh: mergedMesh, bbox: bboxLimit, zoom: zoom, coverRatio: { x: rowRatio, y: colRatio } });
260
+ }
261
+ return result;
262
+ }
263
+ export function mergeMeshes(tiles, mergeCount = 12, tileDimensionLength = 5, processLimit = 6) {
264
+ // filter out tiles with NaN values
265
+ // return returnTop6tiles(tiles);
266
+ const tilesMetaData = createMapAndMetaData(tiles);
267
+ const mergedTiles = mergeTiles(tilesMetaData, mergeCount, tileDimensionLength, processLimit);
268
+ return mergedTiles;
269
+ }
270
+ function returnTop6tiles(tiles) {
271
+ const tilesMetaData = createMapAndMetaData(tiles);
272
+ const hightestZoom = tilesMetaData.maxLevel;
273
+ const result = [];
274
+ let counter = 6;
275
+ function tileToMergedTileInfo(tile) {
276
+ const mesh = new Float32Array(tile.mesh[0].length * tile.mesh[0].length);
277
+ for (let row = 0; row < tile.mesh[0].length; row++) {
278
+ for (let col = 0; col < tile.mesh[0].length; col++) {
279
+ mesh[row * tile.mesh[0].length + col] = tile.mesh[0][row * tile.mesh[0].length + col];
280
+ }
47
281
  }
282
+ return {
283
+ mesh: mesh,
284
+ zoom: tile.level,
285
+ bbox: tile.bbox,
286
+ coverRatio: { x: 1, y: 1 },
287
+ };
48
288
  }
49
- const xCount = maxX - minX + 1;
50
- const yCount = maxY - minY + 1;
51
- const mergedDEM = new Float32Array(xCount * demWidth * yCount * demHeight).fill(0);
52
- for (let tile of tiles) {
53
- _insertDEM(tile.DEM, demWidth, demHeight, mergedDEM, xCount * demWidth, yCount * demHeight, (tile.x - minX) * demWidth, (tile.y - minY) * demHeight);
289
+ for (const tile of tiles) {
290
+ if (counter === 0)
291
+ break;
292
+ if (tile.level === hightestZoom) {
293
+ result.push(tileToMergedTileInfo(tile));
294
+ counter--;
295
+ }
54
296
  }
55
- return mergedDEM;
297
+ return result;
56
298
  }
@@ -8,6 +8,7 @@ import { pointsOnArc } from "../juction/arc-plane";
8
8
  import { latToTileY, tileYtoLat } from "./methods";
9
9
  import { isOnTileEdge } from "./methods";
10
10
  import Delaunator from "delaunator";
11
+ import { radianToMercatorXY } from "../methods";
11
12
  // TODO:get rid of embedded lists. always flat list
12
13
  const TILE_DEM_VERTEX_COUNT = 5; // 5x5 grid for DEM
13
14
  const TILE_DEM_STEPCOUNT = TILE_DEM_VERTEX_COUNT - 1; // 4 inner divisions in each dimension
@@ -271,6 +272,11 @@ export function getAllPoints(triangleMeta, zoom, innerCuts = TILE_DEM_STEPCOUNT)
271
272
  const edgeIndexes = shellPoints.map((e, index) => index * 2); // edge points are always first points in list
272
273
  indices = filteroutEdgeConnections(indices, edgeIndexes);
273
274
  // rotateIndices(indices);
275
+ // for (let i = 0; i < longLatPoints.length / 2; i++) {
276
+ // const xy = radianToMercatorXY([longLatPoints[i * 2], longLatPoints[i * 2 + 1]]);
277
+ // longLatPoints[i * 2] = xy[0];
278
+ // longLatPoints[i * 2 + 1] = xy[1];
279
+ // }
274
280
  return { vec3s: new Float32Array(points), longLats: new Float32Array(longLatPoints), indices: indices };
275
281
  }
276
282
  function fillBetweenInParallels(lat, p1, p2, step, fillVec3Arr, longLatPoints) {
@@ -307,16 +313,6 @@ function filterDuplicate(points) {
307
313
  function showLongLatRadian(longLat) {
308
314
  return `(${(longLat[0] * 180 / Math.PI).toFixed(2)}°, ${(longLat[1] * 180 / Math.PI).toFixed(2)}°)`;
309
315
  }
310
- function rotateIndices(indices) {
311
- const len = indices.length / 3;
312
- let hold;
313
- for (let i = 0; i < len; i++) {
314
- hold = indices[i * 3];
315
- indices[i * 3] = indices[i * 3 + 1];
316
- indices[i * 3 + 1] = hold;
317
- }
318
- return indices;
319
- }
320
316
  // This method for removing external edge connections. The connections are unnecessary and cause a veil descending to the center of sphere from the edges
321
317
  function filteroutEdgeConnections(indexes, filterOutIndexes) {
322
318
  const length = indexes.length / 3;
@@ -377,6 +373,7 @@ export function partialTessellation(triangleMeta, limits, innerCuts) {
377
373
  const allVec3s = [];
378
374
  const allLongLats = [];
379
375
  let pointCounter = 0;
376
+ const stepCount = innerCuts - 1; //
380
377
  /**
381
378
  * Precision for coordinate keys in the map. 1e-12 radians is
382
379
  * extremely small (sub-millimeter on Earth's surface) and avoids
@@ -432,14 +429,16 @@ export function partialTessellation(triangleMeta, limits, innerCuts) {
432
429
  }
433
430
  // 2b. Calculate grid steps for this tile's zoom
434
431
  const tileCount = TILE_COUNTS[zoom];
435
- const lonStep = (2 * Math.PI) / tileCount / innerCuts;
436
- const latStep = 1.0 / innerCuts; // This is in tileY units
432
+ const lonStep = (2 * Math.PI) / tileCount / stepCount;
433
+ const latStep = 1.0 / stepCount; // This is in tileY units
437
434
  // 2c. Generate Latitude (Parallel) Points
438
435
  const startTileY = latToTileY(workBBox.max[1], zoom); // max lat -> min tileY
439
436
  const endTileY = latToTileY(workBBox.min[1], zoom); // min lat -> max tileY
440
- let currentY = Math.ceil(startTileY / latStep) * latStep;
441
- if (currentY < startTileY - 1e-9)
442
- currentY += latStep; // Ensure we start inside or on edge
437
+ // let currentY = Math.ceil(startTileY / latStep) * latStep;
438
+ let currentY = startTileY - Math.abs(startTileY % latStep);
439
+ // if (currentY < startTileY - 1e-9) currentY += latStep; // Ensure we start inside or on edge
440
+ if (currentY === startTileY)
441
+ currentY -= latStep; // since start point is already added
443
442
  while (currentY <= endTileY + 1e-9) {
444
443
  const lat = tileYtoLat(currentY, zoom);
445
444
  // Skip if rounding put us slightly outside the work box
@@ -508,6 +507,14 @@ export function partialTessellation(triangleMeta, limits, innerCuts) {
508
507
  let indices = delaunator.triangles;
509
508
  // 4. Filter out triangles connected to the "shell" points
510
509
  indices = filteroutEdgeConnections(indices, shellPointIndices);
510
+ // console.log(...allLongLats.slice(0, 10));
511
+ for (let i = 0; i < allLongLats.length / 2; i++) {
512
+ const xy = radianToMercatorXY([allLongLats[i * 2], allLongLats[i * 2 + 1]]);
513
+ allLongLats[i * 2] = xy[0];
514
+ allLongLats[i * 2 + 1] = xy[1];
515
+ }
516
+ // show first 5
517
+ // console.log(...allLongLats.slice(0, 10));
511
518
  return {
512
519
  vec3s: new Float32Array(allVec3s),
513
520
  longLats: new Float32Array(allLongLats),
@@ -0,0 +1,67 @@
1
+ function boilerArcInput(start, end, color, key) {
2
+ return {
3
+ key: key,
4
+ start: start,
5
+ end: end,
6
+ color: color,
7
+ height: null,
8
+ msl: null
9
+ };
10
+ }
11
+ export function tileToArcInputs(tiles, color) {
12
+ if (tiles.length === 0)
13
+ return [];
14
+ const result = [];
15
+ // (alias) type ArcInput = {
16
+ // key: string;
17
+ // start: LongLat;
18
+ // end: LongLat;
19
+ // color: Color;
20
+ // height?: Meter | null;
21
+ // msl?: boolean | null;
22
+ // }
23
+ for (const tile of tiles) {
24
+ const tileKey = `tile_${tile.row}_${tile.column}`;
25
+ const bbox = tile.bbox;
26
+ // left side
27
+ result.push(boilerArcInput([bbox.ll.x, bbox.ll.y], [bbox.ll.x, bbox.ur.y], color, `${tileKey}_left`));
28
+ // top side
29
+ result.push(boilerArcInput([bbox.ll.x, bbox.ur.y], [bbox.ur.x, bbox.ur.y], color, `${tileKey}_top`));
30
+ // bottom side
31
+ result.push(boilerArcInput([bbox.ll.x, bbox.ll.y], [bbox.ur.x, bbox.ll.y], color, `${tileKey}_bottom`));
32
+ // right side
33
+ result.push(boilerArcInput([bbox.ur.x, bbox.ll.y], [bbox.ur.x, bbox.ur.y], color, `${tileKey}_right`));
34
+ }
35
+ return result;
36
+ }
37
+ export function mergedTileToArcInputs(mergedTile, color) {
38
+ const result = [];
39
+ const bbox = mergedTile.bbox;
40
+ // left side
41
+ result.push(boilerArcInput([bbox.ll.x, bbox.ll.y], [bbox.ll.x, bbox.ur.y], color, `${mergedTile.zoom}_left`));
42
+ // right side
43
+ result.push(boilerArcInput([bbox.ur.x, bbox.ll.y], [bbox.ur.x, bbox.ur.y], color, `${mergedTile.zoom}_right`));
44
+ // top side
45
+ result.push(boilerArcInput([bbox.ll.x, bbox.ur.y], [bbox.ur.x, bbox.ur.y], color, `${mergedTile.zoom}_top`));
46
+ // bottom side
47
+ result.push(boilerArcInput([bbox.ll.x, bbox.ll.y], [bbox.ur.x, bbox.ll.y], color, `${mergedTile.zoom}_bottom`));
48
+ return result;
49
+ }
50
+ export function tilesToContextWriter4Inputs(tiles) {
51
+ const result = [];
52
+ for (const tile of tiles) {
53
+ const tileKey = `L_${tile.level}_R${tile.row}_C${tile.column}`;
54
+ const centerLong = (tile.bbox.ll.x + tile.bbox.ur.x) / 2;
55
+ const centerLat = (tile.bbox.ll.y + tile.bbox.ur.y) / 2;
56
+ result.push({
57
+ key: tileKey,
58
+ long: centerLong,
59
+ lat: centerLat,
60
+ text: tileKey,
61
+ opacity: null,
62
+ angle: null,
63
+ position: 'center'
64
+ });
65
+ }
66
+ return result;
67
+ }
@@ -0,0 +1,128 @@
1
+ import { ArcOnTerrainPlugin } from "../../../semiplugins/shape-on-terrain/arc-plugin";
2
+ import { tileToArcInputs, tilesToContextWriter4Inputs, mergedTileToArcInputs } from "./adapters";
3
+ import { ContextTextWriter4 } from "../../../write-text/context-text4";
4
+ import { mergeMeshes } from "../../../Math/tessellation/tile-merger";
5
+ // diverse ColorRamp
6
+ const defaultColorsRamp = [
7
+ [0.0, [0, 0, 255, 255]], // Blue
8
+ // [0.3, [255, 165, 0, 255]], // Orange
9
+ [0.25, [0, 255, 0, 255]], // Green
10
+ // [0.4, [255, 0, 0, 255]], // Red
11
+ [0.5, [128, 0, 128, 255]], // Purple
12
+ // [0.6, [0, 255, 255, 255]], // Cyan
13
+ [0.75, [255, 192, 203, 255]], // Pink
14
+ [1.0, [255, 255, 255, 255]], // White
15
+ // [1.0, [0, 0, 0, 255]] // Black
16
+ ];
17
+ export class TilesRendererPlugin {
18
+ id;
19
+ globe;
20
+ gl;
21
+ arcPlugin;
22
+ contextTextWriter;
23
+ colorsRamp;
24
+ _lastTilesUniqueIdentifier = null;
25
+ constructor(id, colorsRamp = defaultColorsRamp) {
26
+ this.globe = null;
27
+ this.gl = null;
28
+ this.contextTextWriter = null;
29
+ this.id = id;
30
+ this.colorsRamp = colorsRamp;
31
+ this.arcPlugin = new ArcOnTerrainPlugin(id + "_arc_plugin", {
32
+ variativeColorsOn: true,
33
+ defaultHeightFromGroundIn3D: 0,
34
+ });
35
+ }
36
+ init(globe, gl) {
37
+ this.globe = globe;
38
+ this.gl = gl;
39
+ this.contextTextWriter = new ContextTextWriter4(globe, (item) => { return [item]; }, (item) => { return item.key; }, (zoomLevel) => (item) => {
40
+ return {
41
+ opacityMultiplier: 1,
42
+ sizeMultiplier: 1
43
+ };
44
+ });
45
+ this.arcPlugin.init(globe, gl);
46
+ }
47
+ draw3D(projectionMatrix, modelViewMatrix, transPosition) {
48
+ // if (this.globe.api_IsScreenMoving()) {
49
+ this._prepareArcInputs();
50
+ // }
51
+ this.arcPlugin.draw3D();
52
+ this.contextTextWriter.draw();
53
+ }
54
+ free() {
55
+ this.arcPlugin.free();
56
+ }
57
+ _prepareArcInputs() {
58
+ // const drawnTiles = filterTilesInScreen(
59
+ // this.globe.api_GetDrawedTilesInfo(),
60
+ // this.globe
61
+ // );
62
+ const drawnTiles = this.globe.api_GetDrawedTilesInfo();
63
+ let maxLevel = 0;
64
+ let minLevel = Number.MAX_VALUE;
65
+ const tileMap = new Map();
66
+ for (const tile of drawnTiles) {
67
+ if (tile.level > maxLevel) {
68
+ maxLevel = tile.level;
69
+ }
70
+ if (tile.level < minLevel) {
71
+ minLevel = tile.level;
72
+ }
73
+ if (!tileMap.has(tile.level)) {
74
+ tileMap.set(tile.level, []);
75
+ }
76
+ tileMap.get(tile.level).push(tile);
77
+ }
78
+ let tilesUniqueId = "";
79
+ for (let level = minLevel; level <= maxLevel; level++) {
80
+ const tiles = tileMap.get(level);
81
+ if (!tiles)
82
+ continue;
83
+ tilesUniqueId += `L${level}_C${tiles.length}_`;
84
+ }
85
+ if (tilesUniqueId === this._lastTilesUniqueIdentifier)
86
+ return;
87
+ this._lastTilesUniqueIdentifier = tilesUniqueId;
88
+ const levelRange = maxLevel - minLevel;
89
+ const colors = this._interpolateColors(levelRange);
90
+ this.arcPlugin.deleteAll();
91
+ for (let level = maxLevel; level >= minLevel; level--) {
92
+ const tiles = tileMap.get(level);
93
+ if (!tiles)
94
+ continue;
95
+ const colorIndex = level - minLevel;
96
+ const color = colors[colorIndex];
97
+ const arcInputs = tileToArcInputs(tiles, color);
98
+ this.arcPlugin.insertBulk(arcInputs);
99
+ }
100
+ // drawMergedBBoxes:
101
+ const mergedTiles = mergeMeshes(drawnTiles, 12, 5, 4);
102
+ const color = [255, 255, 0, 255]; // Yellow
103
+ const arcMergedInputs = mergedTiles.map(mergedTile => mergedTileToArcInputs(mergedTile, color)).flat();
104
+ this.arcPlugin.insertBulk(arcMergedInputs);
105
+ const contextWriterInputs = tilesToContextWriter4Inputs(drawnTiles);
106
+ this.contextTextWriter.clear();
107
+ this.contextTextWriter.insertTextBulk(contextWriterInputs);
108
+ }
109
+ _interpolateColors(maxValue) {
110
+ let colors = [];
111
+ let colorRampIndex = 0;
112
+ for (let i = 0; i <= maxValue; i++) {
113
+ const t = i / maxValue;
114
+ while (colorRampIndex < this.colorsRamp.length - 1 && t > this.colorsRamp[colorRampIndex + 1][0]) {
115
+ colorRampIndex++;
116
+ }
117
+ const [t0, color0] = this.colorsRamp[colorRampIndex];
118
+ const [t1, color1] = this.colorsRamp[Math.min(colorRampIndex + 1, this.colorsRamp.length - 1)];
119
+ const localT = (t - t0) / (t1 - t0);
120
+ const r = Math.round(color0[0] + (color1[0] - color0[0]) * localT);
121
+ const g = Math.round(color0[1] + (color1[1] - color0[1]) * localT);
122
+ const b = Math.round(color0[2] + (color1[2] - color0[2]) * localT);
123
+ // const a = Math.round(color0[3] + (color1[3] - color0[3]) * localT);
124
+ colors.push([r, g, b, 255]);
125
+ }
126
+ return colors;
127
+ }
128
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pirireis/webglobeplugins",
3
- "version": "0.17.0",
3
+ "version": "0.17.1",
4
4
  "main": "index.js",
5
5
  "author": "Toprak Nihat Deniz Ozturk",
6
6
  "license": "MIT",