@pirireis/webglobeplugins 0.17.1 → 1.0.3

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 (67) hide show
  1. package/Math/haversine.js +22 -0
  2. package/Math/methods.js +15 -2
  3. package/Math/tessellation/methods.js +4 -1
  4. package/Math/tessellation/nearest-value-padding.js +112 -0
  5. package/Math/tessellation/spherical-triangle-area.js +99 -0
  6. package/Math/tessellation/tile-merger.js +346 -215
  7. package/Math/tessellation/triangle-tessellation.js +381 -9
  8. package/Math/vec3.js +4 -0
  9. package/Math/xyz-tile.js +18 -0
  10. package/altitude-locator/plugin.js +1 -2
  11. package/investigation-tools/draw/tiles/adapters.js +2 -2
  12. package/investigation-tools/draw/tiles/tiles.js +2 -2
  13. package/package.json +1 -1
  14. package/programs/helpers/fadeaway.js +6 -1
  15. package/programs/point-on-globe/square-pixel-point.js +1 -0
  16. package/programs/polygon-on-globe/texture-dem-triangles.js +94 -116
  17. package/programs/totems/camera-totem-attactment-interface.js +1 -0
  18. package/programs/totems/camerauniformblock.js +33 -22
  19. package/programs/totems/dem-textures-manager.js +265 -0
  20. package/programs/vectorfields/logics/drawrectangleparticles.js +51 -18
  21. package/programs/vectorfields/logics/{ubo-new.js → particle-ubo.js} +5 -14
  22. package/programs/vectorfields/logics/pixelbased.js +42 -36
  23. package/programs/vectorfields/pingpongbuffermanager.js +34 -8
  24. package/semiplugins/shape-on-terrain/terrain-polygon/adapters.js +55 -0
  25. package/semiplugins/shape-on-terrain/terrain-polygon/data/cache.js +102 -0
  26. package/semiplugins/shape-on-terrain/terrain-polygon/data/index-polygon-map.js +45 -0
  27. package/semiplugins/shape-on-terrain/terrain-polygon/data/manager.js +4 -0
  28. package/semiplugins/shape-on-terrain/terrain-polygon/data/master-worker.js +177 -0
  29. package/semiplugins/shape-on-terrain/terrain-polygon/data/polygon-to-triangles.js +100 -0
  30. package/semiplugins/shape-on-terrain/terrain-polygon/data/random.js +121 -0
  31. package/semiplugins/shape-on-terrain/terrain-polygon/data/types.js +1 -0
  32. package/semiplugins/shape-on-terrain/terrain-polygon/data/worker-contact.js +63 -0
  33. package/semiplugins/shape-on-terrain/terrain-polygon/data/worker.js +125 -0
  34. package/semiplugins/shape-on-terrain/terrain-polygon/terrain-polygon.js +219 -0
  35. package/semiplugins/shape-on-terrain/terrain-polygon/types.js +8 -0
  36. package/semiplugins/shell/bbox-renderer/logic.js +18 -58
  37. package/semiplugins/shell/bbox-renderer/object.js +19 -9
  38. package/tracks/point-heat-map/point-to-heat-map-flow.js +1 -1
  39. package/tracks/point-tracks/plugin.js +13 -6
  40. package/tracks/timetracks/program-line-strip.js +1 -1
  41. package/util/account/single-attribute-buffer-management/buffer-manager.js +5 -3
  42. package/util/account/single-attribute-buffer-management/buffer-orchestrator.js +2 -2
  43. package/util/gl-util/uniform-block/manager.js +20 -10
  44. package/util/helper-methods.js +8 -0
  45. package/util/picking/fence.js +4 -2
  46. package/util/picking/picker-displayer.js +51 -9
  47. package/util/programs/draw-texture-on-canvas.js +18 -15
  48. package/util/shaderfunctions/geometrytransformations.js +67 -1
  49. package/vectorfield/waveparticles/plugin.js +241 -116
  50. package/vectorfield/wind/adapters/image-to-fields.js +61 -0
  51. package/vectorfield/wind/adapters/types.js +1 -0
  52. package/vectorfield/wind/imagetovectorfieldandmagnitude.js +6 -9
  53. package/vectorfield/wind/plugin-persistant copy.js +364 -0
  54. package/vectorfield/wind/plugin-persistant.js +375 -0
  55. package/vectorfield/wind/plugin.js +1 -1
  56. package/Math/tessellation/earcut/adapters.js +0 -37
  57. package/Math/tessellation/hybrid-triangle-tessellation-meta.js +0 -123
  58. package/Math/tessellation/shred-input.js +0 -18
  59. package/Math/tessellation/tiler.js +0 -50
  60. package/Math/tessellation/triangle-tessellation-meta.js +0 -523
  61. package/programs/polygon-on-globe/texture-dem-triangle-test-plugin-triangle.js +0 -328
  62. package/programs/vectorfields/logics/drawrectangleparticles1.js +0 -112
  63. package/semiplugins/shape-on-terrain/terrain-cover/texture-dem-cover.js +0 -1
  64. package/util/gl-util/uniform-block/types.js +0 -1
  65. package/util/webglobe/index.js +0 -2
  66. /package/Math/tessellation/{zoom-catch.js → constants.js} +0 -0
  67. /package/util/{webglobe/gldefaultstates.js → globe-default-gl-states.js} +0 -0
@@ -0,0 +1,55 @@
1
+ function geoJSONToTerrainPolygon(geojson, keyAdapter, colorAdapter) {
2
+ if (geojson.type !== 'FeatureCollection') {
3
+ throw new Error('Invalid GeoJSON: Expected FeatureCollection');
4
+ }
5
+ const features = geojson.features;
6
+ const polygons = [];
7
+ for (const feature of features) {
8
+ const geometry = feature.geometry;
9
+ const key = keyAdapter(feature);
10
+ const color = colorAdapter(feature);
11
+ const geometries = [];
12
+ if (geometry.type === 'Polygon') {
13
+ const coordinates = geometry.coordinates;
14
+ const vertices = [];
15
+ const holes = [];
16
+ let holeIndex = 0;
17
+ for (let i = 0; i < coordinates.length; i++) {
18
+ if (i > 0) {
19
+ holeIndex += coordinates[i - 1].length;
20
+ holes.push(holeIndex);
21
+ }
22
+ for (const coord of coordinates[i]) {
23
+ vertices.push(coord[0], coord[1]);
24
+ }
25
+ }
26
+ geometries.push({ vertices, holes });
27
+ }
28
+ else if (geometry.type === 'MultiPolygon') {
29
+ const multiCoordinates = geometry.coordinates;
30
+ for (const coordinates of multiCoordinates) {
31
+ const vertices = [];
32
+ const holes = [];
33
+ let holeIndex = 0;
34
+ for (let i = 0; i < coordinates.length; i++) {
35
+ if (i > 0) {
36
+ holeIndex += coordinates[i - 1].length;
37
+ holes.push(holeIndex);
38
+ }
39
+ for (const coord of coordinates[i]) {
40
+ vertices.push(coord[0], coord[1]);
41
+ }
42
+ }
43
+ geometries.push({ vertices, holes });
44
+ }
45
+ }
46
+ else {
47
+ continue; // Skip non-polygon geometries
48
+ }
49
+ if (geometries.length > 0) {
50
+ polygons.push({ key, color, geometry: geometries });
51
+ }
52
+ }
53
+ return polygons;
54
+ }
55
+ export { geoJSONToTerrainPolygon };
@@ -0,0 +1,102 @@
1
+ import { createTriangleTessellationMeta } from "../../../../Math/tessellation/triangle-tessellation";
2
+ import { triangulation } from "./polygon-to-triangles";
3
+ import RBush from "rbush";
4
+ import { RADIAN } from "../../../../Math/methods";
5
+ // TODO: use type instead of any
6
+ function _entry(polygon, kmThreshold) {
7
+ __polygonInputWGS84ToRadian(polygon);
8
+ const trianglesData = triangulation(polygon, kmThreshold);
9
+ const cacheTriangles = [];
10
+ for (let i = 0; i < trianglesData.indices.length; i += 3) {
11
+ const idx0 = trianglesData.indices[i] * 2;
12
+ const idx1 = trianglesData.indices[i + 1] * 2;
13
+ const idx2 = trianglesData.indices[i + 2] * 2;
14
+ const v0 = [trianglesData.vertices[idx0], trianglesData.vertices[idx0 + 1]];
15
+ const v1 = [trianglesData.vertices[idx1], trianglesData.vertices[idx1 + 1]];
16
+ const v2 = [trianglesData.vertices[idx2], trianglesData.vertices[idx2 + 1]];
17
+ const tessellationMeta = createTriangleTessellationMeta(v0, v1, v2);
18
+ cacheTriangles.push({
19
+ polygon: polygon,
20
+ offset: i / 3,
21
+ tessellationMeta: tessellationMeta,
22
+ minX: tessellationMeta.bbox.min[0],
23
+ minY: tessellationMeta.bbox.min[1],
24
+ maxX: tessellationMeta.bbox.max[0],
25
+ maxY: tessellationMeta.bbox.max[1],
26
+ });
27
+ }
28
+ return cacheTriangles;
29
+ }
30
+ function __polygonInputWGS84ToRadian(polygon) {
31
+ for (const geom of polygon.geometry) {
32
+ const vertices = geom.vertices;
33
+ for (let i = 0; i < vertices.length / 2; i++) {
34
+ vertices[i * 2] = vertices[i * 2] * RADIAN;
35
+ vertices[i * 2 + 1] = vertices[i * 2 + 1] * RADIAN;
36
+ }
37
+ }
38
+ }
39
+ export class Cache {
40
+ cache = new Map();
41
+ kmThreshold;
42
+ rbush = new RBush();
43
+ bboxes = [];
44
+ constructor(kmThreshold) {
45
+ this.kmThreshold = kmThreshold;
46
+ }
47
+ clear() {
48
+ this.cache.clear();
49
+ this.rbush.clear();
50
+ }
51
+ insert(key, polygon) {
52
+ if (this.cache.has(key)) {
53
+ this.remove(key);
54
+ }
55
+ const triangles = _entry(polygon, this.kmThreshold);
56
+ this.rbush.load(triangles);
57
+ this.cache.set(key, { polygon: polygon, triangles });
58
+ }
59
+ remove(key) {
60
+ const entry = this.cache.get(key);
61
+ if (entry) {
62
+ for (const triangle of entry.triangles) {
63
+ this.rbush.remove(triangle);
64
+ }
65
+ this.cache.delete(key);
66
+ }
67
+ }
68
+ setBBOXes(BBOXES) {
69
+ this.bboxes = BBOXES;
70
+ }
71
+ search() {
72
+ const results = new Set();
73
+ for (const bbox of this.bboxes) {
74
+ const { minX, minY, maxX, maxY } = bbox;
75
+ if (minX <= maxX && minY <= maxY) {
76
+ // Normal case
77
+ const partialResults = this.rbush.search({ minX, minY, maxX, maxY });
78
+ for (const res of partialResults) {
79
+ results.add(res);
80
+ }
81
+ }
82
+ else {
83
+ // Handle antimeridian crossing
84
+ // MinX > MaxX case is the signal.
85
+ const partialResults1 = this.rbush.search({ minX, minY, maxX: Math.PI, maxY });
86
+ const partialResults2 = this.rbush.search({ minX: -Math.PI, minY, maxX, maxY });
87
+ for (const res of partialResults1) {
88
+ results.add(res);
89
+ }
90
+ for (const res of partialResults2) {
91
+ results.add(res);
92
+ }
93
+ }
94
+ }
95
+ return results;
96
+ }
97
+ defragment() {
98
+ const allItems = this.rbush.all();
99
+ this.rbush.clear();
100
+ this.rbush.load(allItems);
101
+ }
102
+ }
@@ -0,0 +1,45 @@
1
+ export class IndexPolygonMap {
2
+ _indexPolygonMap = new Map();
3
+ _polygonKeyMap = new Map();
4
+ _thumbstones = [];
5
+ _polygonIndex = 1; // 0 index is null
6
+ constructor() { }
7
+ newIndex() {
8
+ if (this._thumbstones.length > 0) {
9
+ return this._thumbstones.pop();
10
+ }
11
+ return this._polygonIndex++;
12
+ }
13
+ insertBulk(polygons) {
14
+ for (const poly of polygons) {
15
+ if (this._polygonKeyMap.has(poly.key)) {
16
+ this.deleteBulk([poly.key]);
17
+ }
18
+ const index = this.newIndex();
19
+ // @ts-ignore
20
+ poly.index = index; // Assign the new index to the polygon
21
+ this._indexPolygonMap.set(index, poly);
22
+ this._polygonKeyMap.set(poly.key, index);
23
+ }
24
+ return polygons;
25
+ }
26
+ deleteBulk(keys) {
27
+ for (const key of keys) {
28
+ const index = this._polygonKeyMap.get(key);
29
+ if (index === undefined)
30
+ continue;
31
+ this._polygonKeyMap.delete(key);
32
+ this._indexPolygonMap.delete(index);
33
+ this._thumbstones.push(index);
34
+ }
35
+ }
36
+ get(index) {
37
+ return this._indexPolygonMap.get(index) || null;
38
+ }
39
+ clear() {
40
+ this._indexPolygonMap.clear();
41
+ this._polygonKeyMap.clear();
42
+ this._thumbstones = [];
43
+ this._polygonIndex = 1;
44
+ }
45
+ }
@@ -0,0 +1,4 @@
1
+ "use strict";
2
+ // this is the glue binds data to plugin
3
+ // plugin inserts data to this.
4
+ // plugin send bboxes and zoom levels and expects data ready to be looaded on polygon program
@@ -0,0 +1,177 @@
1
+ /// <reference lib="webworker" />
2
+ const _workers = [];
3
+ const _workerMap = new Map();
4
+ let _maxWorkers = 4; // Default, updated in init
5
+ let _nextWorkerIndex = 0;
6
+ // Configuration state to pass to new workers or re-broadcast
7
+ let _pickableState;
8
+ let _variativeColorsOn;
9
+ // Initialize workers
10
+ function initWorkers() {
11
+ _maxWorkers = Math.max(1, (navigator.hardwareConcurrency || 4) - 1);
12
+ for (let i = 0; i < _maxWorkers; i++) {
13
+ addWorker();
14
+ }
15
+ }
16
+ function addWorker() {
17
+ // @ts-ignore
18
+ const worker = new Worker(new URL("./worker.js", import.meta.url), { type: 'module' });
19
+ const wrapper = {
20
+ worker,
21
+ inProgress: false,
22
+ insertDeleteQueue: [],
23
+ lastBBOXData: null,
24
+ itemCount: 0,
25
+ lastOutput: null
26
+ };
27
+ worker.onmessage = (event) => {
28
+ wrapper.inProgress = false;
29
+ wrapper.lastOutput = event.data;
30
+ mergeAndSendResults();
31
+ sendToSubWorker(wrapper);
32
+ };
33
+ // Initialize sub-worker with current global state
34
+ worker.postMessage({
35
+ pickableState: _pickableState,
36
+ variativeColorsOn: _variativeColorsOn,
37
+ insertDeleteQueue: [],
38
+ bboxes: null,
39
+ });
40
+ _workers.push(wrapper);
41
+ }
42
+ function mergeAndSendResults() {
43
+ let totalVec3s = 0;
44
+ let totalIndices = 0;
45
+ let totalLongLats = 0;
46
+ let totalPickIndices = 0;
47
+ let totalVariativeColors = 0;
48
+ const validOutputs = [];
49
+ for (const w of _workers) {
50
+ if (w.lastOutput) {
51
+ validOutputs.push(w.lastOutput);
52
+ totalVec3s += w.lastOutput.vec3s.length;
53
+ totalIndices += w.lastOutput.indices.length;
54
+ totalLongLats += w.lastOutput.longLats.length;
55
+ if (w.lastOutput.pickIndices)
56
+ totalPickIndices += w.lastOutput.pickIndices.length;
57
+ if (w.lastOutput.variativeColors)
58
+ totalVariativeColors += w.lastOutput.variativeColors.length;
59
+ }
60
+ }
61
+ if (validOutputs.length === 0)
62
+ return;
63
+ const merged = {
64
+ vec3s: new Float32Array(totalVec3s),
65
+ indices: new Uint32Array(totalIndices),
66
+ longLats: new Float32Array(totalLongLats),
67
+ pickIndices: totalPickIndices > 0 ? new Float32Array(totalPickIndices) : null,
68
+ variativeColors: totalVariativeColors > 0 ? new Float32Array(totalVariativeColors) : null
69
+ };
70
+ let offsetVec3s = 0;
71
+ let offsetIndices = 0;
72
+ let offsetLongLats = 0;
73
+ let offsetPickIndices = 0;
74
+ let offsetVariativeColors = 0;
75
+ let vertexOffset = 0;
76
+ for (const out of validOutputs) {
77
+ merged.vec3s.set(out.vec3s, offsetVec3s);
78
+ offsetVec3s += out.vec3s.length;
79
+ for (let i = 0; i < out.indices.length; i++) {
80
+ merged.indices[offsetIndices + i] = out.indices[i] + vertexOffset;
81
+ }
82
+ offsetIndices += out.indices.length;
83
+ vertexOffset += out.vec3s.length / 3;
84
+ merged.longLats.set(out.longLats, offsetLongLats);
85
+ offsetLongLats += out.longLats.length;
86
+ if (merged.pickIndices && out.pickIndices) {
87
+ merged.pickIndices.set(out.pickIndices, offsetPickIndices);
88
+ offsetPickIndices += out.pickIndices.length;
89
+ }
90
+ if (merged.variativeColors && out.variativeColors) {
91
+ merged.variativeColors.set(out.variativeColors, offsetVariativeColors);
92
+ offsetVariativeColors += out.variativeColors.length;
93
+ }
94
+ }
95
+ // Send merged result to Main Thread
96
+ self.postMessage(merged, [
97
+ merged.vec3s.buffer,
98
+ merged.indices.buffer,
99
+ merged.longLats.buffer,
100
+ ...(merged.pickIndices ? [merged.pickIndices.buffer] : []),
101
+ ...(merged.variativeColors ? [merged.variativeColors.buffer] : [])
102
+ ]);
103
+ }
104
+ function sendToSubWorker(wrapper) {
105
+ if (wrapper.inProgress)
106
+ return;
107
+ if (wrapper.insertDeleteQueue.length === 0 && wrapper.lastBBOXData === null)
108
+ return;
109
+ wrapper.inProgress = true;
110
+ wrapper.worker.postMessage({
111
+ pickableState: undefined, // State is set on init, only send updates if needed
112
+ variativeColorsOn: undefined,
113
+ bboxes: wrapper.lastBBOXData,
114
+ insertDeleteQueue: wrapper.insertDeleteQueue,
115
+ });
116
+ wrapper.lastBBOXData = null;
117
+ wrapper.insertDeleteQueue = [];
118
+ }
119
+ function triggerAllWorkers() {
120
+ for (const w of _workers) {
121
+ sendToSubWorker(w);
122
+ }
123
+ }
124
+ // Handle messages from Main Thread
125
+ self.onmessage = (event) => {
126
+ const { bboxes, insertDeleteQueue, pickableState, variativeColorsOn } = event.data;
127
+ // Initialize if not already done
128
+ if (_workers.length === 0) {
129
+ _pickableState = pickableState;
130
+ _variativeColorsOn = variativeColorsOn;
131
+ initWorkers();
132
+ }
133
+ // Handle BBOX updates
134
+ if (bboxes) {
135
+ for (const w of _workers) {
136
+ w.lastBBOXData = bboxes;
137
+ }
138
+ }
139
+ // Handle Insert/Delete
140
+ if (insertDeleteQueue && insertDeleteQueue.length > 0) {
141
+ if (insertDeleteQueue[0] === "__CLEAR_ALL_ITEMS__") {
142
+ _workerMap.clear();
143
+ for (const w of _workers) {
144
+ w.insertDeleteQueue = ["__CLEAR_ALL_ITEMS__"];
145
+ w.itemCount = 0;
146
+ w.lastOutput = null;
147
+ }
148
+ }
149
+ else {
150
+ for (const item of insertDeleteQueue) {
151
+ if (typeof item === 'string') {
152
+ // Delete
153
+ const workerIdx = _workerMap.get(item);
154
+ if (workerIdx !== undefined) {
155
+ _workers[workerIdx].insertDeleteQueue.push(item);
156
+ _workers[workerIdx].itemCount--;
157
+ _workerMap.delete(item);
158
+ }
159
+ }
160
+ else {
161
+ // Insert
162
+ let workerIdx = _workerMap.get(item.key);
163
+ if (workerIdx === undefined) {
164
+ workerIdx = _nextWorkerIndex;
165
+ _workerMap.set(item.key, workerIdx);
166
+ _workers[workerIdx].itemCount++;
167
+ _nextWorkerIndex = (_nextWorkerIndex + 1) % _workers.length;
168
+ }
169
+ _workers[workerIdx].insertDeleteQueue.push(item.key);
170
+ }
171
+ }
172
+ }
173
+ }
174
+ // Trigger all workers to process the new data
175
+ triggerAllWorkers();
176
+ };
177
+ export {};
@@ -0,0 +1,100 @@
1
+ /**
2
+ * # Purpose
3
+ * Divide long edges of polygons into smaller segments
4
+ *
5
+ * This algorithm should be called after earcutting / triangulation.
6
+ * Then earcut should run second time to further divide long edges.
7
+ *
8
+ * populate points on a arc between two given points on a sphere
9
+ *
10
+ */
11
+ import { haversine } from "../../../../Math/haversine";
12
+ import earcut from "earcut";
13
+ /**
14
+ *
15
+ */
16
+ function __pointsInBetweenGreateCircle(lat1, lon1, lat2, lon2, unitSphereDistance) {
17
+ const points = [];
18
+ const d1 = haversine(lat1, lon1, lat2, lon2);
19
+ if (d1 <= unitSphereDistance) {
20
+ return null;
21
+ }
22
+ const numSegments = Math.ceil(d1 / unitSphereDistance);
23
+ for (let i = 1; i < numSegments; i++) {
24
+ const f = i / numSegments;
25
+ const A = Math.sin((1 - f) * d1) / Math.sin(d1);
26
+ const B = Math.sin(f * d1) / Math.sin(d1);
27
+ const x = A * Math.cos(lat1) * Math.cos(lon1) + B * Math.cos(lat2) * Math.cos(lon2);
28
+ const y = A * Math.cos(lat1) * Math.sin(lon1) + B * Math.cos(lat2) * Math.sin(lon2);
29
+ const z = A * Math.sin(lat1) + B * Math.sin(lat2);
30
+ const latN = Math.atan2(z, Math.sqrt(x * x + y * y));
31
+ const lonN = Math.atan2(y, x);
32
+ points.push(latN, lonN);
33
+ }
34
+ return points;
35
+ }
36
+ function _dividePolygonEdgeSize(polygon, kmThreshold) {
37
+ const unitSphereDistance = kmThreshold / 6371; // Earth radius in km
38
+ const newGeometries = polygon.geometry.map(geom => {
39
+ const { vertices, holes } = geom;
40
+ const newVertices = [];
41
+ const newHoles = [];
42
+ const processRing = (start, end) => {
43
+ for (let i = start; i < end; i++) {
44
+ const long1 = vertices[2 * i];
45
+ const lat1 = vertices[2 * i + 1];
46
+ const next_i = (i + 1 < end) ? i + 1 : start;
47
+ const long2 = vertices[2 * next_i];
48
+ const lat2 = vertices[2 * next_i + 1];
49
+ newVertices.push(long1, lat1);
50
+ const extraPoints = __pointsInBetweenGreateCircle(lat1, long1, lat2, long2, unitSphereDistance);
51
+ if (extraPoints) {
52
+ newVertices.push(...extraPoints);
53
+ }
54
+ }
55
+ };
56
+ const shellEnd = holes.length > 0 ? holes[0] : vertices.length / 2;
57
+ processRing(0, shellEnd);
58
+ for (let h = 0; h < holes.length; h++) {
59
+ newHoles.push(newVertices.length / 2);
60
+ const holeStart = holes[h];
61
+ const holeEnd = h + 1 < holes.length ? holes[h + 1] : vertices.length / 2;
62
+ processRing(holeStart, holeEnd);
63
+ }
64
+ return { vertices: newVertices, holes: newHoles };
65
+ });
66
+ return {
67
+ ...polygon,
68
+ geometry: newGeometries,
69
+ };
70
+ }
71
+ // TODO:
72
+ // divide TrianglesMiddleData edges longer than threshold
73
+ // function _divideTriangleEdges(
74
+ // key: string,
75
+ // vertices: number[],
76
+ // indices: number[],
77
+ // kmThreshold: number // maybe
78
+ // ): TrianglesMiddleData {
79
+ // return { key, vertices, indices };
80
+ // }
81
+ // TODO: NEED TEST
82
+ function triangulation(polygon, kmThreshold) {
83
+ const finalVertices = [];
84
+ const finalIndices = [];
85
+ let vertexIndexOffset = 0;
86
+ for (const geom of polygon.geometry) {
87
+ const indices = earcut(geom.vertices, geom.holes, 2);
88
+ finalVertices.push(...geom.vertices);
89
+ for (const index of indices) {
90
+ finalIndices.push(index + vertexIndexOffset);
91
+ }
92
+ vertexIndexOffset += geom.vertices.length / 2;
93
+ }
94
+ return {
95
+ key: polygon.key,
96
+ vertices: finalVertices,
97
+ indices: finalIndices
98
+ };
99
+ }
100
+ export { triangulation };
@@ -0,0 +1,121 @@
1
+ /**
2
+ * Generates random polygon data for testing PolygonPluginInput
3
+ */
4
+ /**
5
+ * Generates a random polygon with optional holes
6
+ * @param {Object} options - Configuration options
7
+ * @param {number} options.minVertices - Minimum number of vertices for the shell (default: 3)
8
+ * @param {number} options.maxVertices - Maximum number of vertices for the shell (default: 8)
9
+ * @param {number} options.holeCount - Number of holes to generate (default: 0)
10
+ * @param {number} options.minLat - Minimum latitude (default: -85)
11
+ * @param {number} options.maxLat - Maximum latitude (default: 85)
12
+ * @param {number} options.minLng - Minimum longitude (default: -180)
13
+ * @param {number} options.maxLng - Maximum longitude (default: 180)
14
+ * @param {number} options.dimensions - Number of dimensions per vertex (default: 2)
15
+ * @returns {Object} PolygonPluginInput object
16
+ */
17
+ function generateRandomPolygon(options = {}) {
18
+ const { minVertices = 3, maxVertices = 8, holeCount = 0, minLat = -85, maxLat = 85, minLng = -180, maxLng = 180, dimensions = 2, radius = 4, } = options;
19
+ const key = `polygon_${Date.now()}_${Math.floor(Math.random() * 1000)}_${Math.random()}`;
20
+ const vertices = [];
21
+ const holes = [];
22
+ // Generate shell vertices
23
+ const shellVertexCount = Math.floor(Math.random() * (maxVertices - minVertices + 1)) + minVertices;
24
+ const centerLng = Math.random() * (maxLng - minLng) + minLng;
25
+ const centerLat = Math.random() * (maxLat - minLat) + minLat;
26
+ // Generate shell as a rough circle with random variations
27
+ for (let i = 0; i < shellVertexCount; i++) {
28
+ const angle = (i / shellVertexCount) * 2 * Math.PI;
29
+ const radiusVariation = radius * (0.7 + Math.random() * 0.6); // Add variation to radius
30
+ const lng = centerLng + Math.cos(angle) * radiusVariation;
31
+ const lat = centerLat + Math.sin(angle) * radiusVariation;
32
+ vertices.push(lng, lat);
33
+ if (dimensions > 2) {
34
+ for (let d = 2; d < dimensions; d++) {
35
+ vertices.push(Math.random() * 1000); // Random additional dimensions
36
+ }
37
+ }
38
+ }
39
+ // Generate holes
40
+ for (let h = 0; h < holeCount; h++) {
41
+ const holeStartIndex = vertices.length / dimensions;
42
+ holes.push(holeStartIndex);
43
+ const holeVertexCount = Math.floor(Math.random() * 4) + 3; // 3-6 vertices for holes
44
+ const holeRadius = radius * (0.2 + Math.random() * 0.3); // Smaller radius for holes
45
+ const holeCenterLng = centerLng + (Math.random() - 0.5) * radius * 0.8;
46
+ const holeCenterLat = centerLat + (Math.random() - 0.5) * radius * 0.8;
47
+ for (let i = 0; i < holeVertexCount; i++) {
48
+ const angle = (i / holeVertexCount) * 2 * Math.PI;
49
+ const lng = holeCenterLng + Math.cos(angle) * holeRadius;
50
+ const lat = holeCenterLat + Math.sin(angle) * holeRadius;
51
+ vertices.push(lng, lat);
52
+ if (dimensions > 2) {
53
+ for (let d = 2; d < dimensions; d++) {
54
+ vertices.push(Math.random() * 1000);
55
+ }
56
+ }
57
+ }
58
+ }
59
+ return {
60
+ key,
61
+ geometry: [{ vertices, holes }],
62
+ color: [Math.random(), Math.random(), Math.random(), 1.0],
63
+ };
64
+ }
65
+ /**
66
+ * Generates multiple random polygons
67
+ * @param {number} count - Number of polygons to generate
68
+ * @param {Object} options - Options to pass to generateRandomPolygon
69
+ * @returns {Array} Array of PolygonPluginInput objects
70
+ */
71
+ function generateRandomPolygons(count, options = {}) {
72
+ const polygons = [];
73
+ for (let i = 0; i < count; i++) {
74
+ polygons.push(generateRandomPolygon(options));
75
+ }
76
+ return polygons;
77
+ }
78
+ /**
79
+ * Generates a simple rectangular polygon for basic testing
80
+ * @param {number} lng - Center longitude
81
+ * @param {number} lat - Center latitude
82
+ * @param {number} width - Width in degrees
83
+ * @param {number} height - Height in degrees
84
+ * @returns {Object} PolygonPluginInput object
85
+ */
86
+ function generateRectanglePolygon(lng = 0, lat = 0, width = 2, height = 1) {
87
+ const halfWidth = width / 2;
88
+ const halfHeight = height / 2;
89
+ return {
90
+ key: `rectangle_${Date.now()}`,
91
+ geometry: [{
92
+ vertices: [
93
+ lng - halfWidth, lat - halfHeight, // Bottom-left
94
+ lng + halfWidth, lat - halfHeight, // Bottom-right
95
+ lng + halfWidth, lat + halfHeight, // Top-right
96
+ lng - halfWidth, lat + halfHeight // Top-left
97
+ ],
98
+ holes: []
99
+ }]
100
+ };
101
+ }
102
+ function generateRectangleFromBBOX(bbox, key = null) {
103
+ const { ll, ur } = bbox;
104
+ return {
105
+ key: key || `rectangle_${Date.now()}`,
106
+ geometry: [{
107
+ vertices: [
108
+ ll.x, ll.y, // Bottom-left
109
+ ur.x, ll.y, // Bottom-right
110
+ ur.x, ur.y, // Top-right
111
+ ll.x, ur.y // Top-left
112
+ ],
113
+ holes: []
114
+ }]
115
+ };
116
+ }
117
+ // // Example usage:
118
+ // console.log('Polygon with holes:', generateRandomPolygon({ holeCount: 2 }));
119
+ // console.log('Multiple polygons:', generateRandomPolygons(3));
120
+ // console.log('Rectangle polygon:', generateRectanglePolygon(-74, 40.7, 0.1, 0.05));
121
+ export { generateRandomPolygon, generateRandomPolygons, generateRectanglePolygon, generateRectangleFromBBOX };
@@ -0,0 +1,63 @@
1
+ import { DemTextureManagerCache } from "../../../../programs/totems/dem-textures-manager";
2
+ import { RADIAN } from "../../../../Math/methods";
3
+ export class WorkerContact {
4
+ _masterWorker;
5
+ _options;
6
+ onResult;
7
+ demTextureManager = null;
8
+ constructor(globe, options, onResult) {
9
+ this._options = options;
10
+ this.onResult = onResult;
11
+ // Initialize the Master Worker
12
+ // @ts-ignore
13
+ this._masterWorker = new Worker(new URL("./master-worker.js", import.meta.url), { type: 'module' });
14
+ this._masterWorker.onmessage = (event) => {
15
+ this.onResult(event.data);
16
+ };
17
+ // Initialize Master Worker state
18
+ this._masterWorker.postMessage({
19
+ pickableState: this._options.pickable,
20
+ variativeColorsOn: this._options.variativeColorsOn,
21
+ insertDeleteQueue: [],
22
+ bboxes: null,
23
+ });
24
+ this.demTextureManager = DemTextureManagerCache.get(globe);
25
+ this.demTextureManager.register(this);
26
+ }
27
+ setMergedTiles(mergedTilesInfo) {
28
+ const bboxesData = new Array(mergedTilesInfo.length);
29
+ for (let i = 0; i < mergedTilesInfo.length; i++) {
30
+ const { bbox, zoom } = mergedTilesInfo[i];
31
+ bboxesData[i] = {
32
+ zoom: zoom,
33
+ minX: bbox.ll.x * RADIAN,
34
+ minY: bbox.ll.y * RADIAN,
35
+ maxX: bbox.ur.x * RADIAN,
36
+ maxY: bbox.ur.y * RADIAN,
37
+ };
38
+ }
39
+ // Send BBOX updates to Master Worker
40
+ this._masterWorker.postMessage({
41
+ bboxes: bboxesData
42
+ });
43
+ }
44
+ free() {
45
+ this.demTextureManager.unregister(this);
46
+ this._masterWorker.terminate();
47
+ }
48
+ insertBulk(polygons) {
49
+ this._masterWorker.postMessage({
50
+ insertDeleteQueue: polygons
51
+ });
52
+ }
53
+ deleteBulk(keys) {
54
+ this._masterWorker.postMessage({
55
+ insertDeleteQueue: keys
56
+ });
57
+ }
58
+ clear() {
59
+ this._masterWorker.postMessage({
60
+ insertDeleteQueue: ["__CLEAR_ALL_ITEMS__"]
61
+ });
62
+ }
63
+ }