@pirireis/webglobeplugins 0.16.7 → 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.
Files changed (159) hide show
  1. package/Math/angle-calculation.js +1 -0
  2. package/Math/contour/quadtreecontours.js +300 -0
  3. package/Math/finite-line-2d.js +58 -0
  4. package/Math/methods.js +6 -0
  5. package/Math/tessellation/hybrid-triangle-tessellation-meta.js +123 -0
  6. package/Math/tessellation/tile-merger.js +298 -0
  7. package/Math/tessellation/triangle-tessellation-meta.js +195 -42
  8. package/Math/tessellation/triangle-tessellation.js +5 -1
  9. package/compass-rose/compass-text-writer.js +39 -33
  10. package/constants.js +3 -0
  11. package/{util/heatwavedatamanager → heatwave}/datamanager.js +1 -1
  12. package/heatwave/{plugins/heatwaveglobeshell.js → heatwave.js} +8 -6
  13. package/heatwave/index.js +5 -3
  14. package/heatwave/{isobar/plugin.js → isobar.js} +6 -5
  15. package/{util/heatwavedatamanager → heatwave}/texture-point-sampler.js +24 -3
  16. package/investigation-tools/draw/tiles/adapters.js +67 -0
  17. package/investigation-tools/draw/tiles/tiles.js +128 -0
  18. package/package.json +4 -2
  19. package/programs/arrowfield/{object.js → arrow-field.js} +1 -1
  20. package/programs/arrowfield/logic.js +1 -1
  21. package/programs/data2legend/density-to-legend.js +24 -29
  22. package/programs/data2legend/point-to-density-texture.js +14 -17
  23. package/programs/float2legendwithratio/logic.js +2 -2
  24. package/programs/float2legendwithratio/object.js +1 -1
  25. package/programs/helpers/{blender/program.js → blender.js} +1 -1
  26. package/programs/helpers/{fadeaway/logic.js → fadeaway.js} +11 -2
  27. package/programs/index.js +20 -9
  28. package/programs/line-on-globe/circle-accurate-3d.js +12 -14
  29. package/programs/line-on-globe/circle-accurate-flat.js +0 -1
  30. package/programs/line-on-globe/degree-padding-around-circle-3d.js +13 -15
  31. package/programs/line-on-globe/lines-color-instanced-flat.js +15 -18
  32. package/programs/line-on-globe/naive-accurate-flexible.js +0 -1
  33. package/programs/picking/pickable-polygon-renderer.js +1 -1
  34. package/programs/picking/pickable-renderer.js +2 -2
  35. package/programs/point-on-globe/element-globe-surface-glow.js +2 -2
  36. package/programs/point-on-globe/element-point-glow.js +1 -1
  37. package/programs/point-on-globe/square-pixel-point.js +1 -1
  38. package/programs/polygon-on-globe/texture-dem-triangle-test-plugin-triangle.js +180 -30
  39. package/programs/polygon-on-globe/texture-dem-triangles.js +93 -34
  40. package/programs/rings/partial-ring/piece-of-pie.js +26 -29
  41. package/programs/totems/camerauniformblock.js +31 -42
  42. package/programs/two-d/pixel-padding-for-compass.js +14 -24
  43. package/programs/vectorfields/logics/drawrectangleparticles.js +9 -8
  44. package/programs/vectorfields/logics/drawrectangleparticles1.js +112 -0
  45. package/programs/vectorfields/logics/pixelbased.js +1 -2
  46. package/programs/vectorfields/pingpongbuffermanager.js +1 -1
  47. package/range-tools-on-terrain/bearing-line/adapters.js +1 -1
  48. package/range-tools-on-terrain/circle-line-chain/adapters.js +0 -5
  49. package/range-tools-on-terrain/circle-line-chain/plugin.js +1 -1
  50. package/range-tools-on-terrain/range-ring/plugin.js +4 -6
  51. package/semiplugins/lightweight/line-plugin.js +0 -1
  52. package/semiplugins/shape-on-terrain/arc-plugin.js +9 -2
  53. package/semiplugins/shape-on-terrain/circle-plugin.js +2 -2
  54. package/semiplugins/shape-on-terrain/padding-1-degree.js +1 -1
  55. package/semiplugins/shell/bbox-renderer/index.js +2 -0
  56. package/{programs/globeshell/wiggle → semiplugins/shell/bbox-renderer}/logic.js +101 -102
  57. package/{programs/globeshell/wiggle → semiplugins/shell/bbox-renderer}/object.js +6 -7
  58. package/semiplugins/utility/container-plugin.js +94 -0
  59. package/semiplugins/utility/object-pass-container-plugin.js +80 -0
  60. package/{point-heat-map → tracks/point-heat-map}/adaptors/timetracksplugin-format-to-this.js +1 -1
  61. package/{point-heat-map → tracks/point-heat-map}/plugin-webworker.js +3 -3
  62. package/{point-heat-map → tracks/point-heat-map}/point-to-heat-map-flow.js +11 -14
  63. package/{point-tracks → tracks/point-tracks}/plugin.js +6 -5
  64. package/{timetracks → tracks/timetracks}/adaptors-line-strip.js +1 -1
  65. package/{timetracks → tracks/timetracks}/program-line-strip.js +49 -49
  66. package/{timetracks → tracks/timetracks}/programpoint-line-strip.js +16 -13
  67. package/types.js +6 -0
  68. package/util/account/bufferoffsetmanager.js +1 -1
  69. package/util/account/single-attribute-buffer-management/buffer-orchestrator.js +9 -95
  70. package/util/gl-util/uniform-block/manager.js +0 -1
  71. package/util/gl-util/uniform-block/types.js +0 -7
  72. package/util/index.js +10 -13
  73. package/util/picking/fence.js +16 -18
  74. package/util/picking/picker-displayer.js +6 -8
  75. package/util/programs/{shapesonglobe.js → draw-from-pixel-coords.js} +4 -7
  76. package/util/programs/draw-texture-on-canvas.js +1 -1
  77. package/util/programs/texturetoglobe.js +3 -3
  78. package/util/shaderfunctions/geometrytransformations.js +1 -1
  79. package/vectorfield/arrowfield/index.js +3 -0
  80. package/{arrowfield → vectorfield/arrowfield}/plugin.js +2 -2
  81. package/{waveparticles → vectorfield/waveparticles}/plugin.js +12 -12
  82. package/{wind → vectorfield/wind}/index.js +1 -1
  83. package/{wind → vectorfield/wind}/plugin.js +65 -51
  84. package/write-text/context-text3.js +0 -1
  85. package/write-text/context-text4.js +2 -1
  86. package/write-text/objectarraylabels/index.js +2 -0
  87. package/Math/tessellation/tessellation-algorithm.js +0 -67
  88. package/arrowfield/index.js +0 -3
  89. package/bearing-line/index.js +0 -2
  90. package/bearing-line/plugin.js +0 -444
  91. package/circle-line-chain/chain-list-map.js +0 -201
  92. package/circle-line-chain/init.js +0 -1
  93. package/circle-line-chain/plugin.js +0 -411
  94. package/circle-line-chain/util.js +0 -1
  95. package/compassrose/compassrose.js +0 -293
  96. package/compassrose/index.js +0 -2
  97. package/index.js +0 -12
  98. package/partialrings/buffer-manager.js +0 -75
  99. package/partialrings/index.js +0 -2
  100. package/partialrings/plugin.js +0 -128
  101. package/partialrings/program.js +0 -279
  102. package/programs/arrowfield/index.js +0 -2
  103. package/programs/globe-util/is-globe-moved.js +0 -19
  104. package/programs/globeshell/index.js +0 -2
  105. package/programs/globeshell/wiggle/index.js +0 -2
  106. package/programs/helpers/blender/index.js +0 -1
  107. package/programs/helpers/fadeaway/index.js +0 -2
  108. package/programs/helpers/fadeaway/object.js +0 -14
  109. package/programs/helpers/index.js +0 -2
  110. package/programs/polygon-on-globe/texture-dem-triangle-test-plugin.js +0 -118
  111. package/programs/rings/distancering/circleflatprogram.js +0 -116
  112. package/programs/rings/distancering/circlepaddingfreeangleprogram.js +0 -326
  113. package/programs/rings/distancering/circlepaddysharedbuffer.js +0 -368
  114. package/programs/rings/distancering/index.js +0 -6
  115. package/programs/rings/distancering/paddyflatprogram.js +0 -127
  116. package/programs/rings/distancering/paddyflatprogram2d.js +0 -129
  117. package/programs/rings/distancering/paddyflatprogram3d.js +0 -128
  118. package/programs/two-d/pixel-circle.js +0 -1
  119. package/programs/vectorfields/index.js +0 -3
  120. package/rangerings/enum.js +0 -2
  121. package/rangerings/index.js +0 -5
  122. package/rangerings/plugin.js +0 -543
  123. package/rangerings/rangeringangletext.js +0 -326
  124. package/rangerings/ring-account.js +0 -112
  125. package/timetracks/index.js +0 -1
  126. package/util/build-strategy/general-strategy.js +0 -62
  127. package/util/gl-util/uniform-block/shader.js +0 -1
  128. package/util/heatwavedatamanager/index.js +0 -2
  129. package/util/heatwavedatamanager/pointcoordsmeta.js +0 -22
  130. package/util/jshelpers/data-filler.js +0 -17
  131. package/util/jshelpers/equality.js +0 -18
  132. package/util/jshelpers/index.js +0 -2
  133. package/util/jshelpers/timefilters.js +0 -30
  134. package/util/programs/index.js +0 -1
  135. package/util/surface-line-data/arc-bboxes.js +0 -25
  136. package/util/surface-line-data/arcs-to-cuts.js +0 -50
  137. package/util/surface-line-data/cut-arc.js +0 -1
  138. package/util/surface-line-data/flow.js +0 -28
  139. package/util/surface-line-data/rbush-manager.js +0 -1
  140. package/util/surface-line-data/types.js +0 -1
  141. package/util/surface-line-data/web-worker.js +0 -1
  142. package/util/webglobe/rasteroverlay.js +0 -75
  143. package/write-text/attached-text-writer.js +0 -87
  144. package/write-text/context-text3old.js +0 -152
  145. package/write-text/index.js +0 -1
  146. package/write-text/writer-plugin.js +0 -8
  147. /package/{heatwave/isobar/quadtreecontours.js → Math/contour/quadtreecontours1.js} +0 -0
  148. /package/pin/{pin-object-array.js → pin-object-array1.js} +0 -0
  149. /package/pin/{pin-point-totem.js → pin-point-totem1.js} +0 -0
  150. /package/{point-heat-map → tracks/point-heat-map}/index.js +0 -0
  151. /package/{point-tracks → tracks/point-tracks}/key-methods.js +0 -0
  152. /package/{timetracks → tracks/timetracks}/plugin-line-strip.js +0 -0
  153. /package/{arrowfield → vectorfield/arrowfield}/adaptor.js +0 -0
  154. /package/{waveparticles → vectorfield/waveparticles}/adaptor.js +0 -0
  155. /package/{waveparticles → vectorfield/waveparticles}/index.js +0 -0
  156. /package/{wind → vectorfield/wind}/imagetovectorfieldandmagnitude.js +0 -0
  157. /package/{wind → vectorfield/wind}/vectorfieldimage.js +0 -0
  158. /package/write-text/{context-text.js → context-textDELETE.js} +0 -0
  159. /package/{heatwave/isobar → write-text/objectarraylabels}/objectarraylabels.js +0 -0
@@ -0,0 +1,298 @@
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}`;
8
+ }
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();
21
+ for (const tile of tiles) {
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;
48
+ }
49
+ }
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);
75
+ }
76
+ }
77
+ return newMesh;
78
+ }
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;
100
+ }
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;
120
+ }
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
+ }
255
+ }
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
+ }
281
+ }
282
+ return {
283
+ mesh: mesh,
284
+ zoom: tile.level,
285
+ bbox: tile.bbox,
286
+ coverRatio: { x: 1, y: 1 },
287
+ };
288
+ }
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
+ }
296
+ }
297
+ return result;
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
@@ -118,7 +119,7 @@ function showMeta(triangleMeta) {
118
119
  * @param angle
119
120
  * @param dimension false for longitude (meridian) true for latitude (parallel)
120
121
  */
121
- function getPoints(triangleMeta, angle, dimension) {
122
+ export function getPoints(triangleMeta, angle, dimension) {
122
123
  // console.log("getPoints called with angle:", angle * 180 / Math.PI, "dimension:", dimension ? 'latitude' : 'longitude');
123
124
  // find which arcs are cut by plane
124
125
  const radians = angle;
@@ -147,12 +148,12 @@ function getPoints(triangleMeta, angle, dimension) {
147
148
  }
148
149
  const count = pointsOnArc(arc, _plane, _resultPoints);
149
150
  if (count === 1) {
150
- result.push([..._resultPoints[0]]);
151
+ result.push([[..._resultPoints[0]], vec3ToLongLatRadians(_resultPoints[0])]);
151
152
  }
152
153
  else if (count === 2) {
153
154
  // throw new Error('Unexpected 2 cut points on arc, should be max 1'); // TODO DELETE this line later
154
- result.push([..._resultPoints[0]]);
155
- result.push([..._resultPoints[1]]); // TODO: this is a fix for a bug, need to investigate later
155
+ result.push([[..._resultPoints[0]], vec3ToLongLatRadians(_resultPoints[0])]);
156
+ result.push([[..._resultPoints[1]], vec3ToLongLatRadians(_resultPoints[1])]); // TODO: this is a fix for a bug, need to investigate later
156
157
  }
157
158
  resultsFromDistinctArcs += 1;
158
159
  }
@@ -168,7 +169,7 @@ function getPoints(triangleMeta, angle, dimension) {
168
169
  "bbox:" + triangleMeta.bbox.min[0] * 180 / Math.PI + ", " + triangleMeta.bbox.min[1] * 180 / Math.PI + ", " + triangleMeta.bbox.max[0] * 180 / Math.PI + ", " + triangleMeta.bbox.max[1] * 180 / Math.PI + "\n";
169
170
  showMeta(triangleMeta);
170
171
  for (let res of result) {
171
- const ll = vec3ToLongLatRadians(res);
172
+ const ll = res[1];
172
173
  console.log("Point: " + ll[0] * 180 / Math.PI + ", " + ll[1] * 180 / Math.PI + "\n");
173
174
  }
174
175
  throw new Error(`Unexpected cut count for tile cut, got: ${result.length}, angle: ${angle * 180 / Math.PI}, dimension: ${dimension ? 'latitude' : 'longitude'} \n` + text);
@@ -179,10 +180,10 @@ function getPoints(triangleMeta, angle, dimension) {
179
180
  }
180
181
  result.sort((a, b) => {
181
182
  if (dimension) {
182
- return a[1] - b[1]; // sort by y for longitude cuts
183
+ return a[0][1] - b[0][1]; // sort by y for longitude cuts
183
184
  }
184
185
  else {
185
- return a[0] - b[0]; // sort by x for latitude cuts
186
+ return a[1][0] - b[1][0]; // sort by x for latitude cuts
186
187
  }
187
188
  });
188
189
  return result;
@@ -239,15 +240,13 @@ export function getAllPoints(triangleMeta, zoom, innerCuts = TILE_DEM_STEPCOUNT)
239
240
  for (let i = 0; i < concurances.length; i += 2) {
240
241
  const p0 = concurances[i];
241
242
  const p1 = concurances[i + 1];
242
- points.push(...p0);
243
- points.push(...p1);
244
- const p0LongLat = vec3ToLongLatRadians(p0);
245
- const p1LongLat = vec3ToLongLatRadians(p1);
246
- longLatPoints.push(...p0LongLat);
247
- longLatPoints.push(...p1LongLat);
248
- fillBetweenInParallels(lat, p0LongLat, p1LongLat, lonLengthRadian, points, longLatPoints);
249
- if (length(p0) < 0.99 || length(p1) < 0.99 || length(p0) > 1.01 || length(p1) > 1.01) {
250
- console.warn("Warning: Cut point is not on unit sphere!", length(p0), length(p1));
243
+ points.push(...p0[0]);
244
+ longLatPoints.push(...p0[1]);
245
+ fillBetweenInParallels(lat, p0[1], p1[1], lonLengthRadian, points, longLatPoints);
246
+ points.push(...p1[0]);
247
+ longLatPoints.push(...p1[1]);
248
+ if (length(p0[0]) < 0.99 || length(p1[0]) < 0.99 || length(p0[0]) > 1.01 || length(p1[0]) > 1.01) {
249
+ console.warn("Warning: Cut point is not on unit sphere!", length(p0[0]), length(p1[0]));
251
250
  }
252
251
  }
253
252
  currentY -= latInnerStep;
@@ -258,15 +257,13 @@ export function getAllPoints(triangleMeta, zoom, innerCuts = TILE_DEM_STEPCOUNT)
258
257
  currentLong += lonLengthRadian; // since start point is already added
259
258
  while (currentLong < endMeridianRadian) {
260
259
  const [p0, p1] = getPoints(triangleMeta, currentLong, false);
261
- const p0LongLat = vec3ToLongLatRadians(p0);
262
- const p1LongLat = vec3ToLongLatRadians(p1);
263
- if (!isOnTileEdge(p0LongLat, zoom)) {
264
- points.push(...p0);
265
- longLatPoints.push(...p0LongLat);
260
+ if (!isOnTileEdge(p0[1], zoom)) {
261
+ points.push(...p0[0]);
262
+ longLatPoints.push(...p0[1]);
266
263
  }
267
- if (!isOnTileEdge(p1LongLat, zoom)) {
268
- points.push(...p1);
269
- longLatPoints.push(...p1LongLat);
264
+ if (!isOnTileEdge(p1[1], zoom)) {
265
+ points.push(...p1[0]);
266
+ longLatPoints.push(...p1[1]);
270
267
  }
271
268
  currentLong += lonLengthRadian;
272
269
  }
@@ -274,7 +271,12 @@ export function getAllPoints(triangleMeta, zoom, innerCuts = TILE_DEM_STEPCOUNT)
274
271
  let indices = delaunator.triangles;
275
272
  const edgeIndexes = shellPoints.map((e, index) => index * 2); // edge points are always first points in list
276
273
  indices = filteroutEdgeConnections(indices, edgeIndexes);
277
- rotateIndices(indices);
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
+ // }
278
280
  return { vec3s: new Float32Array(points), longLats: new Float32Array(longLatPoints), indices: indices };
279
281
  }
280
282
  function fillBetweenInParallels(lat, p1, p2, step, fillVec3Arr, longLatPoints) {
@@ -292,18 +294,18 @@ function fillBetweenInParallels(lat, p1, p2, step, fillVec3Arr, longLatPoints) {
292
294
  current += step;
293
295
  }
294
296
  }
295
- function filterDuplicate(vec3s) {
296
- const result = [];
297
+ function filterDuplicate(points) {
298
+ const result = [points[0]];
297
299
  let dublicate = false;
298
- for (let i = 0; i < vec3s.length; i++) {
300
+ for (let i = 1; i < points.length; i++) {
299
301
  for (let j = 0; j < result.length; j++) {
300
- if (equals(vec3s[i], result[j])) {
302
+ if (equals(points[i][0], result[j][0])) {
301
303
  dublicate = true;
302
304
  break;
303
305
  }
304
306
  }
305
307
  if (!dublicate) {
306
- result.push(vec3s[i]);
308
+ result.push(points[i]);
307
309
  }
308
310
  }
309
311
  return result;
@@ -311,16 +313,6 @@ function filterDuplicate(vec3s) {
311
313
  function showLongLatRadian(longLat) {
312
314
  return `(${(longLat[0] * 180 / Math.PI).toFixed(2)}°, ${(longLat[1] * 180 / Math.PI).toFixed(2)}°)`;
313
315
  }
314
- function rotateIndices(indices) {
315
- const len = indices.length / 3;
316
- let hold;
317
- for (let i = 0; i < len; i++) {
318
- hold = indices[i * 3];
319
- indices[i * 3] = indices[i * 3 + 1];
320
- indices[i * 3 + 1] = hold;
321
- }
322
- return indices;
323
- }
324
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
325
317
  function filteroutEdgeConnections(indexes, filterOutIndexes) {
326
318
  const length = indexes.length / 3;
@@ -366,5 +358,166 @@ function shredTriangleTessellationMeta(triangleMeta, zoomLevel) {
366
358
  *
367
359
  * still all triangles should have some sort of cached tessellation to fast render on rapit earth rotation
368
360
  */
369
- // function partialTessellation(triangleMeta: TriangleTessellationMeta, limits: BBox[], zoomLevel: number, innerCuts: number): { vec3s: Float32Array, longLats: Float32Array, indices: Uint32Array } {
370
- // }
361
+ /**
362
+ * Creates a mesh for a spherical triangle, tessellated only within the
363
+ * regions specified by the `limits` array.
364
+ *
365
+ * @param triangleMeta The metadata for the spherical triangle.
366
+ * @param limits An array of bounding boxes and their associated zoom levels to tessellate.
367
+ * @param innerCuts The number of subdivisions within each tile (e.g., TILE_DEM_STEPCOUNT).
368
+ * @returns A set of arrays (vec3s, longLats, indices) representing the partial mesh.
369
+ */
370
+ export function partialTessellation(triangleMeta, limits, innerCuts) {
371
+ // TODO: pointMap can be local variable and cleaned after function call to avoid reinitialization overhead
372
+ const pointMap = new Map(); // Key: "lon|lat", Value: index
373
+ const allVec3s = [];
374
+ const allLongLats = [];
375
+ let pointCounter = 0;
376
+ const stepCount = innerCuts - 1; //
377
+ /**
378
+ * Precision for coordinate keys in the map. 1e-12 radians is
379
+ * extremely small (sub-millimeter on Earth's surface) and avoids
380
+ * floating point inaccuracies causing duplicate points.
381
+ */
382
+ const KEY_PRECISION = 10;
383
+ /**
384
+ * Adds a point to the vertex arrays if it doesn't already exist.
385
+ * Returns the index of the point (new or existing).
386
+ */
387
+ const addPoint = (longLat, vec3) => {
388
+ const key = `${longLat[0].toPrecision(KEY_PRECISION)}|${longLat[1].toPrecision(KEY_PRECISION)}`;
389
+ let index = pointMap.get(key);
390
+ if (index !== undefined) {
391
+ return index; // Point already exists
392
+ }
393
+ // Point is new, add it
394
+ const v3 = vec3 || createUnitVectorFromLongLat(longLat);
395
+ allVec3s.push(...v3);
396
+ allLongLats.push(...longLat);
397
+ index = pointCounter++;
398
+ pointMap.set(key, index);
399
+ return index;
400
+ };
401
+ // 1. Add triangle's "shell" (for Delaunay) and "edge" (vertices/limits) points
402
+ // These points constrain the triangulation.
403
+ const shellPointIndices = [];
404
+ const edgePoints = getEdgePoints(triangleMeta);
405
+ // Assuming shellPoints and edgePoints have the same length and correspond
406
+ const shellPointCount = Math.min(triangleMeta.shellPoints.length, edgePoints.length);
407
+ for (let i = 0; i < shellPointCount; i++) {
408
+ // Add the "shell" point (for Delaunay stability)
409
+ shellPointIndices.push(addPoint(triangleMeta.shellPoints[i]));
410
+ // Add the actual "edge" point (triangle vertex or limit point)
411
+ addPoint(vec3ToLongLatRadians(edgePoints[i]), edgePoints[i]);
412
+ }
413
+ // 2. Iterate through each tile limit
414
+ for (const { bbox: tileBBox, zoom } of limits) {
415
+ // 2a. Calculate the intersection BBOX (the area we'll work on)
416
+ const workBBox = {
417
+ min: [
418
+ Math.max(triangleMeta.bbox.min[0], tileBBox.min[0]),
419
+ Math.max(triangleMeta.bbox.min[1], tileBBox.min[1])
420
+ ],
421
+ max: [
422
+ Math.min(triangleMeta.bbox.max[0], tileBBox.max[0]),
423
+ Math.min(triangleMeta.bbox.max[1], tileBBox.max[1])
424
+ ],
425
+ };
426
+ // If the intersection is invalid (no overlap), skip this tile
427
+ if (workBBox.min[0] >= workBBox.max[0] || workBBox.min[1] >= workBBox.max[1]) {
428
+ continue;
429
+ }
430
+ // 2b. Calculate grid steps for this tile's zoom
431
+ const tileCount = TILE_COUNTS[zoom];
432
+ const lonStep = (2 * Math.PI) / tileCount / stepCount;
433
+ const latStep = 1.0 / stepCount; // This is in tileY units
434
+ // 2c. Generate Latitude (Parallel) Points
435
+ const startTileY = latToTileY(workBBox.max[1], zoom); // max lat -> min tileY
436
+ const endTileY = latToTileY(workBBox.min[1], zoom); // min lat -> max tileY
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
442
+ while (currentY <= endTileY + 1e-9) {
443
+ const lat = tileYtoLat(currentY, zoom);
444
+ // Skip if rounding put us slightly outside the work box
445
+ if (lat < workBBox.min[1] - 1e-9 || lat > workBBox.max[1] + 1e-9) {
446
+ currentY += latStep;
447
+ continue;
448
+ }
449
+ // Find where this latitude line intersects the triangle
450
+ const intersections = getPoints(triangleMeta, lat, true);
451
+ for (let i = 0; i < intersections.length; i += 2) {
452
+ const p0 = intersections[i]; // [Vec3, LongLatRadian]
453
+ const p1 = intersections[i + 1];
454
+ // Find the segment of this latitude line that is *inside* both
455
+ // the triangle (p0-p1) AND the tile BBOX (workBBox)
456
+ const triMinLon = Math.min(p0[1][0], p1[1][0]);
457
+ const triMaxLon = Math.max(p0[1][0], p1[1][0]);
458
+ const segMinLon = Math.max(workBBox.min[0], triMinLon);
459
+ const segMaxLon = Math.min(workBBox.max[0], triMaxLon);
460
+ // If this clipped segment is valid, generate points
461
+ if (segMinLon < segMaxLon - 1e-9) {
462
+ // Add the exact start and end points of the clipped segment
463
+ addPoint([segMinLon, lat]);
464
+ addPoint([segMaxLon, lat]);
465
+ // Add fill points *between* them
466
+ let currentLon = Math.ceil(segMinLon / lonStep) * lonStep;
467
+ if (currentLon < segMinLon - 1e-9)
468
+ currentLon += lonStep;
469
+ while (currentLon < segMaxLon - 1e-9) {
470
+ addPoint([currentLon, lat]);
471
+ currentLon += lonStep;
472
+ }
473
+ }
474
+ }
475
+ currentY += latStep;
476
+ }
477
+ // 2d. Generate Longitude (Meridian) Points
478
+ const startLon = workBBox.min[0];
479
+ const endLon = workBBox.max[0];
480
+ let currentLon = Math.ceil(startLon / lonStep) * lonStep;
481
+ if (currentLon < startLon - 1e-9)
482
+ currentLon += lonStep;
483
+ while (currentLon <= endLon + 1e-9) {
484
+ // Find where this longitude line intersects the triangle
485
+ const intersections = getPoints(triangleMeta, currentLon, false);
486
+ for (const p of intersections) {
487
+ const pLongLat = p[1];
488
+ // Check if this point is inside the *workBBox*
489
+ if (pLongLat[1] >= workBBox.min[1] - 1e-9 &&
490
+ pLongLat[1] <= workBBox.max[1] + 1e-9) {
491
+ addPoint(pLongLat, p[0]);
492
+ }
493
+ }
494
+ currentLon += lonStep;
495
+ }
496
+ } // End loop over limits
497
+ // 3. Triangulate
498
+ // We must have at least 3 points to make a triangle
499
+ if (pointCounter < 3) {
500
+ return {
501
+ vec3s: new Float32Array(),
502
+ longLats: new Float32Array(),
503
+ indices: new Uint32Array(),
504
+ };
505
+ }
506
+ const delaunator = new Delaunator(allLongLats);
507
+ let indices = delaunator.triangles;
508
+ // 4. Filter out triangles connected to the "shell" points
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));
518
+ return {
519
+ vec3s: new Float32Array(allVec3s),
520
+ longLats: new Float32Array(allLongLats),
521
+ indices: indices,
522
+ };
523
+ }
@@ -2,9 +2,13 @@
2
2
  * @author Toprak Ozturk
3
3
  *
4
4
  */
5
- import { createTriangleTessellationMeta, getAllPoints } from "./triangle-tessellation-meta";
5
+ import { createTriangleTessellationMeta, getAllPoints, partialTessellation } from "./triangle-tessellation-meta";
6
6
  export function test1(zoomLevel, p1, p2, p3) {
7
7
  const triangleMeta = createTriangleTessellationMeta(p1, p2, p3);
8
8
  const { vec3s, longLats, indices } = getAllPoints(triangleMeta, zoomLevel);
9
9
  return { vec3s, longLats, indices };
10
10
  }
11
+ export function partialTest(bboxZooms, p1, p2, p3) {
12
+ const triangleMeta = createTriangleTessellationMeta(p1, p2, p3);
13
+ return partialTessellation(triangleMeta, bboxZooms, 5);
14
+ }