@pirireis/webglobeplugins 1.3.0 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Math/methods.js CHANGED
@@ -76,6 +76,13 @@ export const wgs84ToCartesian3d = (output, long, lat, height) => {
76
76
  output[1] = y * radius;
77
77
  output[2] = z * radius;
78
78
  };
79
+ export const unitVectorFromLongLat = (output, long, lat) => {
80
+ const longRad = long * RADIAN;
81
+ const latRad = lat * RADIAN;
82
+ output[0] = Math.cos(latRad) * Math.cos(longRad);
83
+ output[1] = Math.cos(latRad) * Math.sin(longRad);
84
+ output[2] = Math.sin(latRad);
85
+ };
79
86
  export const wgs84ToMercator = (long, lat) => {
80
87
  return [
81
88
  WORLD_RADIUS_MERCATOR * long * RADIAN,
package/constants.js CHANGED
@@ -4,3 +4,4 @@ export const CSRenderPassPluginKeys = ["supportNormalPass",
4
4
  export const GLOBE_DEFAULT_MIN_LOD = 2;
5
5
  export const GLOBE_DEFAULT_MAX_LOD = 25;
6
6
  export const POLE2 = 12756274.0;
7
+ export const POLE_BY_PI = 6378136.99911;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pirireis/webglobeplugins",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "main": "index.js",
5
5
  "author": "Toprak Nihat Deniz Ozturk",
6
6
  "license": "MIT",
@@ -1,36 +1,92 @@
1
1
  import { createProgram } from "../../util/webglobjectbuilders";
2
- import { CameraUniformBlockTotemCache, CameraUniformBlockString } from "../totems";
3
- import { mercatorXYToGLPosition, cartesian3DToGLPosition } from "../../util/shaderfunctions/geometrytransformations";
2
+ import { CameraUniformBlockTotemCache, CameraUniformBlockString } from "../totems/camerauniformblock";
3
+ import { mercatorXYToGLPosition, cartesian3DToGLPosition, POLE_BY_PI, relativeBBoxPositionRadian } from "../../util/shaderfunctions/geometrytransformations";
4
4
  import { noRegisterGlobeProgramCache } from "../programcache";
5
- const vs = `#version 300 es
5
+ import { DEM_TEXTURE_BLOCK_STRING, DemTextureManagerCache } from "../totems/attachments/dem-textures-manager";
6
+ import { UniformBlockManager } from "../../util/gl-util/uniform-block/manager";
7
+ import { WORLD_RADIUS_3D } from "../../Math/constants";
8
+ const uniformBindingPoints = Object.freeze({
9
+ camera: 0,
10
+ style: 1,
11
+ dem: 2,
12
+ });
13
+ const styleBlockManager = new UniformBlockManager('StyleBlock', [
14
+ { name: "opacity", type: "float", value: new Float32Array([1.0]) },
15
+ { name: "pointSize", type: "float", value: new Float32Array([2.0]) },
16
+ { name: "hoveredPointSize", type: "float", value: new Float32Array([4.0]) },
17
+ { name: "hovered_vertexID", type: "int", value: new Int32Array([-1]) },
18
+ ], uniformBindingPoints.style);
19
+ const DEM_TEXTURE_GLPOSITION_SCRIPT = `
20
+ const float elevation = ${WORLD_RADIUS_3D};
21
+ // altitude = 0.0 / 0.0; can raise "invalid operation" before the calculation,
22
+ // so altitude is 0.0 and hasDemSample is used to determine validity.
23
+ float altitude = 0.0;
24
+ bool hasDemSample = false;
25
+ if (is3D == true) {
26
+ for (int i = 0; i < 6; i++) {
27
+ if( i == u_breakLoopIndex - 1 ) {
28
+ break;
29
+ }
30
+ vec2 uv = relativeBBoxPositionRadian(pos2D, u_demTextureBBOX[i]);
31
+ if (uv.x != -1.0) {
32
+ uv.y = 1.0 - uv.y; // flip y coordinate
33
+ // rescale and transform uv to fit in the texture data cover bbox
34
+ uv *= u_textureDataCoverRatio[i].xy;
35
+
36
+ //
37
+ altitude = texture(u_demTexture, vec3(uv, float(i))).r;
38
+ hasDemSample = true;
39
+ break;
40
+ }
41
+ }
42
+
43
+ // If the geometry is outside all given bboxes, force NaN so the primitive gets discarded.
44
+ if (!hasDemSample) {
45
+ altitude = 0.0 / 0.0;
46
+ }
47
+ vec3 position = pos3D * (elevation + (altitude + height) * elevation_scale);
48
+ gl_Position = cartesian3DToGLPosition(position);
49
+ } else {
50
+ vec2 mercatorXY = pos2D * POLE_BY_PI;
51
+ gl_Position = mercatorXYToGLPosition(mercatorXY);
52
+ }
53
+ `;
54
+ const vsF = (fitMode) => `#version 300 es
55
+ #pragma vscode_glsllint_stage : vert
56
+
57
+ precision highp float;
58
+ precision highp sampler2DArray;
59
+
60
+ ${fitMode ? DEM_TEXTURE_BLOCK_STRING : ""}
6
61
  ${CameraUniformBlockString}
62
+ ${styleBlockManager.glslCode()}
7
63
  ${mercatorXYToGLPosition}
8
64
  ${cartesian3DToGLPosition}
65
+ ${relativeBBoxPositionRadian}
66
+ ${POLE_BY_PI}
67
+
68
+
9
69
  precision highp float;
10
70
  precision highp int;
11
71
 
12
- uniform int hovered_vertexID; // can be removed
13
-
14
72
  in vec3 pos3D;
15
73
  in vec2 pos2D;
16
74
  in vec4 rgba;
17
-
18
- uniform float pointSize;
19
- uniform float hoveredPointSize;
75
+ ${fitMode ? "in float height;" : ""}
20
76
 
21
77
  flat out highp int vVertexID;
22
78
 
23
-
24
79
  out vec4 v_rgba;
25
80
 
26
81
  void main() {
27
82
 
83
+ ${fitMode ? DEM_TEXTURE_GLPOSITION_SCRIPT : `
28
84
  if(is3D){
29
85
  gl_Position = cartesian3DToGLPosition(pos3D);
30
- }
31
- else{
86
+ } else {
32
87
  gl_Position = mercatorXYToGLPosition(pos2D);
33
88
  }
89
+ `}
34
90
  if (hovered_vertexID == gl_VertexID) {
35
91
  gl_PointSize = hoveredPointSize;
36
92
  } else {
@@ -40,9 +96,12 @@ void main() {
40
96
  vVertexID = gl_VertexID;
41
97
  }`;
42
98
  const fs = `#version 300 es
99
+ #pragma vscode_glsllint_stage : frag
100
+
43
101
  precision highp float;
102
+ precision highp int;
44
103
 
45
- uniform float opacity;
104
+ ${styleBlockManager.glslCode()}
46
105
 
47
106
  flat in highp int vVertexID;
48
107
  in vec4 v_rgba;
@@ -56,46 +115,46 @@ void main() {
56
115
  fragColor.a *= opacity;
57
116
  }`;
58
117
  class PointOnGlobeProgram {
59
- constructor(globe) {
118
+ globe;
119
+ gl;
120
+ program;
121
+ demTextureManager = null; // assigned in constructor if fitMode is Dynamic, otherwise remains undefined
122
+ cameraBlockTotem;
123
+ fitMode;
124
+ _publishedUBOs = [];
125
+ _isFreed = false;
126
+ constructor(globe, fitMode) {
60
127
  this.globe = globe;
61
128
  this.gl = globe.gl;
129
+ if (fitMode != "GroundElevation" && fitMode != "SeaLevel") {
130
+ throw new Error(`Invalid fitMode: ${fitMode}. Supported values are "GroundElevation" and "SeaLevel".`);
131
+ }
132
+ this.fitMode = (fitMode === "GroundElevation");
133
+ const vs = vsF(this.fitMode);
62
134
  this.program = createProgram(this.gl, vs, fs);
63
- const { gl, program } = this;
64
- this.uniforms = {
65
- opacity: gl.getUniformLocation(program, "opacity"),
66
- hovered_vertexID: gl.getUniformLocation(program, "hovered_vertexID"),
67
- hoveredPointSize: gl.getUniformLocation(program, "hoveredPointSize"),
68
- pointSize: gl.getUniformLocation(program, "pointSize"),
69
- };
70
- // eslint-disable-next-line
71
- { // assign opacity
72
- this._lastOpacity = 1.0;
73
- this._lastPointSize = 2.0;
74
- this._lastHoveredPointSize = 4.0;
75
- this._lastHoveredID = -1;
76
- const currentProgram = gl.getParameter(gl.CURRENT_PROGRAM);
77
- gl.useProgram(program);
78
- gl.uniform1f(this.uniforms.opacity, this._lastOpacity);
79
- gl.uniform1f(this.uniforms.pointSize, this._lastPointSize);
80
- gl.uniform1f(this.uniforms.hoveredPointSize, this._lastHoveredPointSize);
81
- gl.uniform1i(this.uniforms.hovered_vertexID, this._lastHoveredID);
82
- gl.useProgram(currentProgram);
135
+ if (this.fitMode) {
136
+ this.demTextureManager = DemTextureManagerCache.get(globe);
137
+ this.demTextureManager.assignBindingPoint(this.program, uniformBindingPoints.dem);
83
138
  }
139
+ const { gl, program } = this;
140
+ styleBlockManager.assignBindingPoint(this.gl, this.program);
141
+ const styleBlockIndex = gl.getUniformBlockIndex(program, 'StyleBlock');
142
+ gl.uniformBlockBinding(program, styleBlockIndex, uniformBindingPoints.style);
84
143
  // eslint-disable-next-line
85
144
  { // assign attribute locations
86
145
  gl.bindAttribLocation(program, 0, "pos3D");
87
146
  gl.bindAttribLocation(program, 1, "pos2D");
88
147
  gl.bindAttribLocation(program, 2, "rgba");
148
+ this.fitMode && gl.bindAttribLocation(program, 3, "height");
89
149
  }
90
150
  // eslint-disable-next-line
91
151
  { // arrange camera uniform block
92
- this.cameraBlockBingingPoint = 0;
93
152
  this.cameraBlockTotem = CameraUniformBlockTotemCache.get(globe);
94
153
  const cameraBlockIndex = gl.getUniformBlockIndex(program, "CameraUniformBlock");
95
- gl.uniformBlockBinding(program, cameraBlockIndex, this.cameraBlockBingingPoint);
154
+ gl.uniformBlockBinding(program, cameraBlockIndex, uniformBindingPoints.camera);
96
155
  }
97
156
  }
98
- createVAO(pos3DBuffer, pos2DBuffer, rgbaBuffer) {
157
+ createVAO(pos3DBuffer, pos2DBuffer, rgbaBuffer, heightBuffer) {
99
158
  const { gl } = this;
100
159
  const vao = gl.createVertexArray();
101
160
  gl.bindVertexArray(vao);
@@ -118,33 +177,35 @@ class PointOnGlobeProgram {
118
177
  gl.vertexAttribPointer(2, 4, gl.FLOAT, false, 0, 0);
119
178
  // gl.vertexAttribPointer(2, 4, gl.UNSIGNED_INT, true, 0, 0);
120
179
  }
180
+ if (this.fitMode) {
181
+ if (heightBuffer) {
182
+ gl.bindBuffer(gl.ARRAY_BUFFER, heightBuffer || null);
183
+ gl.enableVertexAttribArray(3);
184
+ gl.vertexAttribPointer(3, 1, gl.FLOAT, false, 0, 0);
185
+ }
186
+ else {
187
+ gl.disableVertexAttribArray(3);
188
+ gl.vertexAttrib4f(3, 0, 0, 0, 1);
189
+ }
190
+ }
121
191
  gl.bindVertexArray(null);
122
192
  gl.bindBuffer(gl.ARRAY_BUFFER, null);
123
193
  return vao;
124
194
  }
125
- draw(vao, length, { opacity = 1.0, hoveredID = -1, pointSize = 2.0, hoveredPointSize = 4.0, elementBuffer = null } = {}) {
126
- if (length === 0 || opacity === 0)
195
+ createUBO(bufferReadType = "DYNAMIC_DRAW") {
196
+ const ubo = styleBlockManager.createUBO(this.gl, bufferReadType);
197
+ this._publishedUBOs.push(ubo);
198
+ return ubo;
199
+ }
200
+ draw(vao, length, drawOptionsUBO, elementBuffer = null) {
201
+ if (length === 0)
127
202
  return;
128
- const { gl, program, uniforms } = this;
203
+ const { gl, program } = this;
129
204
  gl.useProgram(program);
130
- if (this._lastOpacity !== opacity) {
131
- gl.uniform1f(uniforms.opacity, opacity);
132
- this._lastOpacity = opacity;
133
- }
134
- if (this._lastPointSize !== pointSize) {
135
- gl.uniform1f(uniforms.pointSize, pointSize);
136
- this._lastPointSize = pointSize;
137
- }
138
- if (this._lastHoveredPointSize !== hoveredPointSize) {
139
- gl.uniform1f(uniforms.hoveredPointSize, hoveredPointSize);
140
- this._lastHoveredPointSize = hoveredPointSize;
141
- }
142
- if (this._lastHoveredID !== hoveredID) {
143
- gl.uniform1i(uniforms.hovered_vertexID, hoveredID);
144
- this._lastHoveredID = hoveredID;
145
- }
146
205
  gl.bindVertexArray(vao);
147
- this.cameraBlockTotem.bind(this.cameraBlockBingingPoint);
206
+ this.cameraBlockTotem.bind(uniformBindingPoints.camera);
207
+ drawOptionsUBO.bind();
208
+ this.fitMode && this.demTextureManager.bindData(0, uniformBindingPoints.dem);
148
209
  if (elementBuffer) {
149
210
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, elementBuffer);
150
211
  gl.drawElements(gl.POINTS, length, gl.UNSIGNED_INT, 0);
@@ -153,20 +214,29 @@ class PointOnGlobeProgram {
153
214
  else {
154
215
  gl.drawArrays(gl.POINTS, 0, length);
155
216
  }
156
- this.cameraBlockTotem.unbind(this.cameraBlockBingingPoint);
217
+ this.fitMode && this.demTextureManager.unbindData(0, uniformBindingPoints.dem);
218
+ drawOptionsUBO.unbind();
219
+ this.cameraBlockTotem.unbind(uniformBindingPoints.camera);
157
220
  gl.bindVertexArray(null);
158
221
  }
159
222
  free() {
160
223
  if (this._isFreed)
161
224
  return;
162
225
  const { gl, globe } = this;
226
+ this._publishedUBOs.forEach(ubo => ubo.free());
227
+ this._publishedUBOs = [];
163
228
  CameraUniformBlockTotemCache.release(globe);
229
+ this.fitMode && DemTextureManagerCache.release(globe);
164
230
  gl.deleteProgram(this.program);
165
231
  this._isFreed = true;
166
232
  }
167
233
  }
168
234
  const PointOnGlobeProgramCache = Object.freeze({
169
- get: (globe) => noRegisterGlobeProgramCache.getProgram(globe, PointOnGlobeProgram),
170
- release: (globe) => noRegisterGlobeProgramCache.releaseProgram(globe, PointOnGlobeProgram)
235
+ get(globe, fitMode) {
236
+ return noRegisterGlobeProgramCache.getProgram(globe, PointOnGlobeProgram, fitMode);
237
+ },
238
+ release(globe, fitMode) {
239
+ noRegisterGlobeProgramCache.releaseProgram(globe, PointOnGlobeProgram, fitMode);
240
+ }
171
241
  });
172
242
  export { PointOnGlobeProgramCache };
@@ -89,32 +89,35 @@ const glProgramCache = (function () {
89
89
  })();
90
90
  const noRegisterGlobeProgramCache = (function () {
91
91
  const cache = new Map();
92
- function getProgram(globe, ProgramClass) {
92
+ const keyFunction = (globe, ProgramClass, mode) => `${globe.id}_${ProgramClass.name}_${mode}`;
93
+ function getProgram(globe, ProgramClass, mode) {
94
+ const key = keyFunction(globe, ProgramClass, mode);
93
95
  if (!cache.has(globe)) {
94
96
  cache.set(globe, new Map());
95
97
  }
96
98
  ;
97
- if (!cache.get(globe).has(ProgramClass)) {
98
- cache.get(globe).set(ProgramClass, {
99
- program: new ProgramClass(globe),
99
+ if (!cache.get(globe).has(key)) {
100
+ cache.get(globe).set(key, {
101
+ program: new ProgramClass(globe, mode),
100
102
  count: 1
101
103
  });
102
104
  }
103
105
  else {
104
- cache.get(globe).get(ProgramClass).count++;
106
+ cache.get(globe).get(key).count++;
105
107
  }
106
- return cache.get(globe).get(ProgramClass).program;
108
+ return cache.get(globe).get(key).program;
107
109
  }
108
110
  ;
109
- function releaseProgram(globe, ProgramClass) {
110
- if (cache.has(globe) && cache.get(globe).has(ProgramClass)) {
111
- if (cache.get(globe).get(ProgramClass).count === 0) {
111
+ function releaseProgram(globe, ProgramClass, mode) {
112
+ const key = keyFunction(globe, ProgramClass, mode);
113
+ if (cache.has(globe) && cache.get(globe).has(key)) {
114
+ if (cache.get(globe).get(key).count === 0) {
112
115
  throw new Error("The program counter is already 0, cannot release program");
113
116
  }
114
- cache.get(globe).get(ProgramClass).count--;
115
- if (cache.get(globe).get(ProgramClass).count === 0) {
116
- cache.get(globe).get(ProgramClass).program.free();
117
- cache.get(globe).delete(ProgramClass);
117
+ cache.get(globe).get(key).count--;
118
+ if (cache.get(globe).get(key).count === 0) {
119
+ cache.get(globe).get(key).program.free();
120
+ cache.get(globe).delete(key);
118
121
  }
119
122
  }
120
123
  }
@@ -174,6 +174,7 @@ export class DemTextureManager {
174
174
  for (const obj of this.registry) {
175
175
  obj.setMergedTiles(this.mergedData);
176
176
  }
177
+ console.log("DEM Texture Manager updated with new merged tiles data");
177
178
  }
178
179
  bindData(textureUnit, bindingPoint) {
179
180
  const gl = this._globe.gl;
@@ -0,0 +1 @@
1
+ "use strict";
@@ -1,31 +1,51 @@
1
- import { BufferOrchestrator, BufferManager, ObjectStore } from "../../util/account/index";
1
+ import { BufferOrchestrator, BufferManager, ObjectStore } from "../../util/account/single-attribute-buffer-management/index";
2
2
  import { PickerDisplayer } from "../../util/picking/picker-displayer";
3
- import { PointOnGlobeProgramCache } from "../../programs/point-on-globe/square-pixel-point";
4
- import { defaultblendfunction } from "../../util/globe-default-gl-states";
5
- import { wgs84ToCartesian3d, wgs84ToMercator } from "../../Math/methods";
3
+ import { PointOnGlobeProgramCache, } from "../../programs/point-on-globe/square-pixel-point";
4
+ import { wgs84ToCartesian3d, wgs84ToMercator, unitVectorFromLongLat } from "../../Math/methods";
5
+ import { POLE_BY_PI } from "../../constants";
6
6
  const _0vec3 = /* @__PURE__ */ [0, 0, 0];
7
- /**
8
- * @typedef {number} long
9
- * @typedef {number} lat
10
- * @typedef {number} height
11
- * @typedef {string} ID
12
- * @typedef {string} trackID
13
- * @typedef {[number, number, number, number]} rgba 0-1
14
- * @typedef { long ,lat, height, ID } Point
15
- * @typedef {Array<Point>, rgba, trackID} Track
16
- */
17
7
  class PointTracksPlugin {
18
- constructor(id, { pointSize = 2, hoveredPointSize = 4, selectionPointFilling = 4, opacity = 1.0, objectStoreExtraParameters = ["long", "lat", "height"], initialCapacity = 1000 } = {}) {
8
+ id;
9
+ globe = null;
10
+ gl = null;
11
+ program;
12
+ pointSizes;
13
+ _isFreed;
14
+ _useElevationWithTerrainFit;
15
+ _pointProgram;
16
+ _vao;
17
+ _StorageManagersMap = null;
18
+ _bufferOrchestrator = null;
19
+ _pickerDisplayer;
20
+ _selectedID;
21
+ _terrainFitMode;
22
+ _terrainFit;
23
+ _selectedObj;
24
+ _tracksToPointsMap;
25
+ _opacity;
26
+ _lastWH;
27
+ _focusParams;
28
+ _objectStoreExtraParameters;
29
+ _objectStoreExtraParametersFiltered;
30
+ _drawOptionsUBO;
31
+ _lastSelectedID = -1;
32
+ constructor(id, { terrainFitMode = "SeaLevel", useElevationWithTerrainFit = false, pointSize = 2, hoveredPointSize = 4, selectionPointFilling = 4, opacity = 1.0, objectStoreExtraParameters = ["long", "lat", "height"], initialCapacity = 1000 } = {}) {
19
33
  this.id = id;
20
34
  this._isFreed = false;
35
+ this._useElevationWithTerrainFit = useElevationWithTerrainFit;
21
36
  this._pointProgram = null;
22
37
  this._vao = null;
23
- this._bufferManagersMap = null;
24
- this._bufferOrchestrator = null;
25
38
  this._pickerDisplayer = null;
26
39
  this._selectedID = -1;
40
+ // check _terrainFitMode
41
+ if (terrainFitMode !== "SeaLevel" && terrainFitMode !== "GroundElevation") {
42
+ console.warn(`Invalid terrainFitMode: ${terrainFitMode}. Falling back to "SeaLevel".`);
43
+ throw new Error(`Invalid terrainFitMode: ${terrainFitMode}. Valid options are "SeaLevel" or "GroundElevation".`);
44
+ }
45
+ this._terrainFitMode = terrainFitMode;
46
+ this._terrainFit = terrainFitMode === "GroundElevation";
27
47
  this._selectedObj = null;
28
- this._tracksToPointsMap = new Map(); // one to many
48
+ this._tracksToPointsMap = new Map();
29
49
  this._opacity = opacity ? opacity : 1.0;
30
50
  this.program = null;
31
51
  this._lastWH = { w: 0, h: 0 };
@@ -45,7 +65,14 @@ class PointTracksPlugin {
45
65
  this.globe = globe;
46
66
  this.gl = gl;
47
67
  this._pickerDisplayer = new PickerDisplayer(globe);
48
- this._pointProgram = PointOnGlobeProgramCache.get(globe);
68
+ this._pointProgram = PointOnGlobeProgramCache.get(globe, this._terrainFitMode);
69
+ this._drawOptionsUBO = this._pointProgram.createUBO();
70
+ this._lastSelectedID = -1;
71
+ this._drawOptionsUBO.update(new Map([
72
+ ["pointSize", this.pointSizes.pointSize],
73
+ ["hoveredPointSize", this.pointSizes.hoveredPointSize],
74
+ ["opacity", this._opacity],
75
+ ]));
49
76
  this._focusParams.elementBuffer = gl.createBuffer();
50
77
  this._initBufferManagers();
51
78
  }
@@ -53,26 +80,31 @@ class PointTracksPlugin {
53
80
  const { gl } = this;
54
81
  const initialCapacity = this._bufferOrchestrator.capacity;
55
82
  const bufferType = "DYNAMIC_DRAW";
56
- this._bufferManagersMap = new Map([
83
+ this._StorageManagersMap = new Map([
57
84
  ["pos3D", {
58
85
  bufferManager: new BufferManager(gl, 3, { bufferType, initialCapacity }),
59
- adaptor: (item) => {
60
- wgs84ToCartesian3d(_0vec3, item.long, item.lat, item.height / 1000); // height is in meters
61
- return new Float32Array(_0vec3); // height is in meters
62
- }
86
+ adaptor: this._terrainFit ?
87
+ (item) => {
88
+ unitVectorFromLongLat(_0vec3, item.long, item.lat);
89
+ return new Float32Array(_0vec3);
90
+ } :
91
+ (item) => {
92
+ wgs84ToCartesian3d(_0vec3, item.long, item.lat, item.height / 1000);
93
+ return new Float32Array(_0vec3);
94
+ }
63
95
  }],
64
96
  ["pos2D", {
65
97
  bufferManager: new BufferManager(gl, 2, { bufferType, initialCapacity }),
66
- adaptor: (item) => new Float32Array(wgs84ToMercator(item.long, item.lat))
98
+ adaptor: this._terrainFit ? (item) => {
99
+ const xy = new Float32Array(wgs84ToMercator(item.long, item.lat));
100
+ xy[0] /= POLE_BY_PI;
101
+ xy[1] /= POLE_BY_PI;
102
+ return xy;
103
+ } :
104
+ (item) => new Float32Array(wgs84ToMercator(item.long, item.lat))
67
105
  }],
68
106
  ["rgba", {
69
107
  bufferManager: new BufferManager(gl, 4, { bufferType, initialCapacity, typedArrayConstructor: Float32Array }),
70
- // adaptor: (item) => new Uint8Array([
71
- // item.rgba[0] * 255,
72
- // item.rgba[1] * 255,
73
- // item.rgba[2] * 255,
74
- // item.rgba[3] * 255
75
- // ])
76
108
  adaptor: (item) => new Float32Array(item.rgba)
77
109
  }],
78
110
  ["objectStore", {
@@ -92,38 +124,48 @@ class PointTracksPlugin {
92
124
  }
93
125
  }]
94
126
  ]);
95
- this._vao = this._pointProgram.createVAO(this._bufferManagersMap.get("pos3D").bufferManager.buffer, this._bufferManagersMap.get("pos2D").bufferManager.buffer, this._bufferManagersMap.get("rgba").bufferManager.buffer);
127
+ if (this._terrainFit && this._useElevationWithTerrainFit) {
128
+ this._StorageManagersMap.set("height", {
129
+ bufferManager: new BufferManager(gl, 1, { bufferType, initialCapacity }),
130
+ adaptor: (item) => new Float32Array([item.height / 1000])
131
+ });
132
+ }
133
+ this._vao = this._pointProgram.createVAO(this._StorageManagersMap.get("pos3D").bufferManager.buffer, this._StorageManagersMap.get("pos2D").bufferManager.buffer, this._StorageManagersMap.get("rgba").bufferManager.buffer, (this._terrainFit && this._useElevationWithTerrainFit) ? this._StorageManagersMap.get("height").bufferManager.buffer : undefined);
96
134
  }
97
135
  setPointSize(size) {
98
136
  this.pointSizes.pointSize = size;
137
+ this._drawOptionsUBO.update({ pointSize: size });
99
138
  this.globe?.DrawRender();
100
139
  }
101
140
  setSelectionPointFilling(size) {
102
141
  this.pointSizes.selectionPointFilling = size;
142
+ this._drawOptionsUBO.update({ selectionPointFilling: size });
103
143
  this.globe?.DrawRender();
104
144
  }
105
145
  setHoveredPointSize(size) {
106
146
  this.pointSizes.hoveredPointSize = size;
147
+ this._drawOptionsUBO.update({ hoveredPointSize: size });
107
148
  this.globe?.DrawRender();
108
149
  }
109
150
  setOpacity(opacity) {
110
151
  if (opacity < 0 || opacity > 1)
111
152
  return;
112
153
  this._opacity = opacity;
154
+ this._drawOptionsUBO.update({ opacity });
113
155
  this.globe?.DrawRender();
114
156
  }
115
157
  getCurrentSelection() {
116
158
  return this._selectedObj;
117
159
  }
118
160
  /**
119
- *
120
- * @param {*} x screen x
121
- * @param {*} y screen y
122
- * @param {*} callback callback on selection
161
+ * Select points at screen coordinates
162
+ * @param x screen x coordinate
163
+ * @param y screen y coordinate
164
+ * @param callback callback on selection
123
165
  */
124
166
  screenSelection(x, y, callback) {
125
167
  const { pointSizes, _pickerDisplayer } = this;
126
- const objectStore = this._bufferManagersMap.get("objectStore").bufferManager;
168
+ const objectStore = this._StorageManagersMap.get("objectStore").bufferManager;
127
169
  const wrapper = (selectedIDsSet) => {
128
170
  const selectedIDs = Array.from(selectedIDsSet);
129
171
  if (selectedIDs.length === 0) {
@@ -147,8 +189,8 @@ class PointTracksPlugin {
147
189
  _pickerDisplayer.pickXY(x, y, pointSizes.selectionPointFilling, wrapper);
148
190
  }
149
191
  /**
150
- * @param {Array<Track>} tracks
151
- * @returns
192
+ * Insert multiple tracks at once
193
+ * @param tracks Array of tracks to insert
152
194
  */
153
195
  insertBulk(tracks) {
154
196
  this._fillTracksToPointsMap(tracks);
@@ -157,21 +199,22 @@ class PointTracksPlugin {
157
199
  if (currentLoad + flattenedPoints.length >= 2147483647) {
158
200
  throw new Error("Too many points, Point count cannot exceed 2147483647");
159
201
  }
160
- const { _bufferManagersMap, _bufferOrchestrator } = this;
161
- _bufferOrchestrator.insertBulk(flattenedPoints, _bufferManagersMap);
202
+ const { _StorageManagersMap, _bufferOrchestrator } = this;
203
+ _bufferOrchestrator.insertBulk(flattenedPoints, _StorageManagersMap);
162
204
  this._refillFocus();
163
205
  this.globe?.DrawRender();
164
206
  }
165
207
  flush() {
166
208
  const capacity = 100;
167
209
  this._bufferOrchestrator?.flush({ capacity });
168
- this._bufferManagersMap.forEach(({ bufferManager }) => bufferManager.resetWithCapacity(capacity));
210
+ this._StorageManagersMap.forEach(({ bufferManager }) => bufferManager.free());
169
211
  this._turnOffFocus();
170
212
  this._tracksToPointsMap.clear();
171
213
  this.globe?.DrawRender();
172
214
  }
173
215
  /**
174
- * @param {string} trackID
216
+ * Delete a track by ID
217
+ * @param trackID The track ID to delete
175
218
  */
176
219
  deleteTrack(trackID) {
177
220
  const pointList = this._tracksToPointsMap.get(trackID);
@@ -179,26 +222,29 @@ class PointTracksPlugin {
179
222
  console.warn(`Track with ID ${trackID} does not exist.`);
180
223
  return;
181
224
  }
182
- ;
183
225
  const points = Array.from(pointList);
184
- const { _bufferOrchestrator, _bufferManagersMap } = this;
185
- _bufferOrchestrator.deleteBulk(points.map((pointID) => keyMethod(trackID, pointID)), _bufferManagersMap);
226
+ const { _bufferOrchestrator, _StorageManagersMap } = this;
227
+ _bufferOrchestrator.deleteBulk(points.map((pointID) => keyMethod(trackID, pointID)), _StorageManagersMap);
186
228
  this._tracksToPointsMap.delete(trackID);
187
- this._redraw = true;
188
229
  this._refillFocus();
189
230
  this.globe?.DrawRender();
190
231
  }
191
232
  /**
192
- * @param {string} trackID
193
- * @param {Array<string>} pointIDs
233
+ * Delete specific points from a track
234
+ * @param trackID The track ID
235
+ * @param pointIDs Array of point IDs to delete
194
236
  */
195
237
  deletePoints(trackID, pointIDs) {
196
- const { _bufferOrchestrator, _bufferManagersMap } = this;
197
- _bufferOrchestrator.deleteBulk(pointIDs.map((pointID) => keyMethod(trackID, pointID)), _bufferManagersMap);
238
+ const { _bufferOrchestrator, _StorageManagersMap } = this;
239
+ _bufferOrchestrator.deleteBulk(pointIDs.map((pointID) => keyMethod(trackID, pointID)), _StorageManagersMap);
198
240
  this._deletePointsFromTracksMap(trackID, pointIDs);
199
241
  this._refillFocus();
200
242
  this.globe?.DrawRender();
201
243
  }
244
+ /**
245
+ * Focus on specific tracks (or disable focus with null)
246
+ * @param trackIDs Array of track IDs to focus on, or null to disable focus
247
+ */
202
248
  focusTracks(trackIDs = null) {
203
249
  if (!this.globe)
204
250
  return;
@@ -214,6 +260,11 @@ class PointTracksPlugin {
214
260
  }
215
261
  this.globe.DrawRender();
216
262
  }
263
+ /**
264
+ * Update the color of a track
265
+ * @param trackID The track ID
266
+ * @param rgba The new RGBA color values (0-1)
267
+ */
217
268
  updateTrackColor(trackID, rgba) {
218
269
  if (!this._tracksToPointsMap.has(trackID)) {
219
270
  console.warn(`Track with ID ${trackID} does not exist.`);
@@ -221,13 +272,13 @@ class PointTracksPlugin {
221
272
  }
222
273
  const pointList = this._tracksToPointsMap.get(trackID);
223
274
  const points = Array.from(pointList);
224
- const { _bufferOrchestrator, _bufferManagersMap } = this;
275
+ const { _bufferOrchestrator, _StorageManagersMap } = this;
225
276
  _bufferOrchestrator.updateBulk(points.map((pointID) => {
226
277
  return {
227
278
  key: keyMethod(trackID, pointID),
228
279
  rgba: rgba
229
280
  };
230
- }), _bufferManagersMap, ["rgba"]);
281
+ }), _StorageManagersMap, ["rgba"]);
231
282
  this._refillFocus();
232
283
  this.globe?.DrawRender();
233
284
  }
@@ -237,9 +288,9 @@ class PointTracksPlugin {
237
288
  return;
238
289
  this._isFreed = true;
239
290
  this._pickerDisplayer.free();
240
- PointOnGlobeProgramCache.release(this.globe);
291
+ PointOnGlobeProgramCache.release(this.globe, this._terrainFitMode);
241
292
  this.gl.deleteBuffer(this._focusParams.elementBuffer);
242
- this._bufferManagersMap.forEach(({ bufferManager, adaptor }) => bufferManager.free());
293
+ this._StorageManagersMap.forEach(({ bufferManager }) => bufferManager.free());
243
294
  }
244
295
  draw3D() {
245
296
  const { gl, _pointProgram, _pickerDisplayer, _bufferOrchestrator, _vao } = this;
@@ -253,21 +304,10 @@ class PointTracksPlugin {
253
304
  _pickerDisplayer.bindFBO();
254
305
  _pickerDisplayer.clearTextures();
255
306
  if (this._focusParams.on) {
256
- _pointProgram.draw(_vao, this._focusParams.length, {
257
- hoveredID: this._selectedID,
258
- opacity: this._opacity,
259
- pointSize: this.pointSizes.pointSize,
260
- hoveredPointSize: this.pointSizes.hoveredPointSize,
261
- elementBuffer: this._focusParams.elementBuffer
262
- });
307
+ _pointProgram.draw(_vao, this._focusParams.length, this._drawOptionsUBO, this._focusParams.elementBuffer);
263
308
  }
264
309
  else {
265
- _pointProgram.draw(_vao, _bufferOrchestrator.length, {
266
- hoveredID: this._selectedID,
267
- opacity: this._opacity,
268
- pointSize: this.pointSizes.pointSize,
269
- hoveredPointSize: this.pointSizes.hoveredPointSize
270
- });
310
+ _pointProgram.draw(_vao, _bufferOrchestrator.length, this._drawOptionsUBO, null);
271
311
  }
272
312
  gl.bindFramebuffer(gl.FRAMEBUFFER, null);
273
313
  _pickerDisplayer.drawColorTexture();
@@ -332,6 +372,20 @@ class PointTracksPlugin {
332
372
  const x = pos.canvasX;
333
373
  const y = globe.api_ScrH() - pos.canvasY;
334
374
  this.screenSelection(x, y, (selectedPoints) => {
375
+ if (selectedPoints.length > 0) {
376
+ if (this._lastSelectedID !== this._selectedID) {
377
+ this._lastSelectedID = this._selectedID;
378
+ this._drawOptionsUBO.updateSingle("hovered_vertexID", this._selectedID);
379
+ this.globe.DrawRender();
380
+ }
381
+ }
382
+ else {
383
+ if (this._lastSelectedID !== -1) {
384
+ this._lastSelectedID = -1;
385
+ this._drawOptionsUBO.updateSingle("hovered_vertexID", -1);
386
+ this.globe.DrawRender();
387
+ }
388
+ }
335
389
  });
336
390
  }
337
391
  _turnOffFocus() {
@@ -0,0 +1,437 @@
1
+ import { BufferOrchestrator, BufferManager, ObjectStore } from "../../util/account/index";
2
+ import { PickerDisplayer } from "../../util/picking/picker-displayer";
3
+ import { PointOnGlobeProgramCache, } from "../../programs/point-on-globe/square-pixel-point";
4
+ import { defaultblendfunction } from "../../util/globe-default-gl-states";
5
+ import { wgs84ToCartesian3d, wgs84ToMercator, unitVectorFromLongLat } from "../../Math/methods";
6
+ import { POLE_BY_PI } from "../../constants";
7
+ const _0vec3 = /* @__PURE__ */ [0, 0, 0];
8
+ /**
9
+ * @typedef {number} long
10
+ * @typedef {number} lat
11
+ * @typedef {number} height
12
+ * @typedef {string} ID
13
+ * @typedef {string} trackID
14
+ * @typedef {[number, number, number, number]} rgba 0-1
15
+ * @typedef { long ,lat, height, ID } Point
16
+ * @typedef {Array<Point>, rgba, trackID} Track
17
+ */
18
+ class PointTracksPlugin {
19
+ constructor(id, { terrainFitMode = "SeaLevel", // "SeaLevel" or "GroundElevation"
20
+ useElevationWithTerrainFit = false, // opens a buffer
21
+ pointSize = 2, hoveredPointSize = 4, selectionPointFilling = 4, opacity = 1.0, objectStoreExtraParameters = ["long", "lat", "height"], initialCapacity = 1000 } = {}) {
22
+ this.id = id;
23
+ this._isFreed = false;
24
+ this._useElevationWithTerrainFit = useElevationWithTerrainFit;
25
+ this._pointProgram = null;
26
+ this._vao = null;
27
+ this._bufferManagersMap = null;
28
+ this._bufferOrchestrator = null;
29
+ this._pickerDisplayer = null;
30
+ this._selectedID = -1;
31
+ // check _terrainFitMode
32
+ if (terrainFitMode !== "SeaLevel" && terrainFitMode !== "GroundElevation") {
33
+ console.warn(`Invalid terrainFitMode: ${terrainFitMode}. Falling back to "SeaLevel".`);
34
+ throw new Error(`Invalid terrainFitMode: ${terrainFitMode}. Valid options are "SeaLevel" or "GroundElevation".`);
35
+ }
36
+ this._terrainFitMode = terrainFitMode;
37
+ this._terrainFit = terrainFitMode === "GroundElevation";
38
+ this._selectedObj = null;
39
+ this._tracksToPointsMap = new Map(); // one to many
40
+ this._opacity = opacity ? opacity : 1.0;
41
+ this.program = null;
42
+ this._lastWH = { w: 0, h: 0 };
43
+ this._focusParams = { on: false, length: 0, elementBuffer: null, trackIDs: [] };
44
+ this.pointSizes = {
45
+ pointSize: pointSize ? pointSize : 2,
46
+ selectionPointFilling: selectionPointFilling ? selectionPointFilling : 4,
47
+ hoveredPointSize: hoveredPointSize ? hoveredPointSize : 4
48
+ };
49
+ this._objectStoreExtraParameters = objectStoreExtraParameters ? objectStoreExtraParameters : [];
50
+ this._objectStoreExtraParametersFiltered = objectStoreExtraParameters.filter(param => {
51
+ return !["long", "lat", "height", "ID", "trackID"].includes(param);
52
+ });
53
+ this._bufferOrchestrator = new BufferOrchestrator({ capacity: initialCapacity ? initialCapacity : 1000 });
54
+ }
55
+ init(globe, gl) {
56
+ this.globe = globe;
57
+ this.gl = gl;
58
+ this._pickerDisplayer = new PickerDisplayer(globe);
59
+ this._pointProgram = PointOnGlobeProgramCache.get(globe, this._terrainFitMode);
60
+ this._drawOptionsUBO = this._pointProgram.createUBO();
61
+ this._lastSelectedID = -1;
62
+ this._drawOptionsUBO.update(new Map([
63
+ ["pointSize", this.pointSizes.pointSize],
64
+ ["hoveredPointSize", this.pointSizes.hoveredPointSize],
65
+ ["opacity", this._opacity],
66
+ ]));
67
+ this._focusParams.elementBuffer = gl.createBuffer();
68
+ this._initBufferManagers();
69
+ }
70
+ _initBufferManagers() {
71
+ const { gl } = this;
72
+ const initialCapacity = this._bufferOrchestrator.capacity;
73
+ const bufferType = "DYNAMIC_DRAW";
74
+ this._bufferManagersMap = new Map([
75
+ ["pos3D", {
76
+ bufferManager: new BufferManager(gl, 3, { bufferType, initialCapacity }),
77
+ adaptor: this._terrainFit ?
78
+ (item) => {
79
+ unitVectorFromLongLat(_0vec3, item.long, item.lat); // height is in meters
80
+ return new Float32Array(_0vec3); // height is in meters
81
+ } :
82
+ (item) => {
83
+ wgs84ToCartesian3d(_0vec3, item.long, item.lat, item.height / 1000); // height is in meters
84
+ return new Float32Array(_0vec3); // height is in meters
85
+ }
86
+ }],
87
+ ["pos2D", {
88
+ bufferManager: new BufferManager(gl, 2, { bufferType, initialCapacity }),
89
+ adaptor: this._terrainFit ? (item) => {
90
+ const xy = new Float32Array(wgs84ToMercator(item.long, item.lat));
91
+ xy[0] /= POLE_BY_PI;
92
+ xy[1] /= POLE_BY_PI;
93
+ return xy;
94
+ } :
95
+ (item) => new Float32Array(wgs84ToMercator(item.long, item.lat))
96
+ }],
97
+ ["rgba", {
98
+ bufferManager: new BufferManager(gl, 4, { bufferType, initialCapacity, typedArrayConstructor: Float32Array }),
99
+ adaptor: (item) => new Float32Array(item.rgba)
100
+ }],
101
+ ["objectStore", {
102
+ bufferManager: new ObjectStore({ initialCapacity }),
103
+ adaptor: (item) => {
104
+ const result = {
105
+ trackID: item.trackID,
106
+ pointID: item.pointID,
107
+ };
108
+ // Add extra parameters specified in _objectStoreExtraParameters
109
+ this._objectStoreExtraParameters.forEach(param => {
110
+ if (item.hasOwnProperty(param)) {
111
+ result[param] = item[param];
112
+ }
113
+ });
114
+ return result;
115
+ }
116
+ }]
117
+ ]);
118
+ if (this._terrainFit && this._useElevationWithTerrainFit) {
119
+ this._bufferManagersMap.set("height", {
120
+ bufferManager: new BufferManager(gl, 1, { bufferType, initialCapacity }),
121
+ adaptor: (item) => new Float32Array([item.height / 1000])
122
+ });
123
+ }
124
+ this._vao = this._pointProgram.createVAO(this._bufferManagersMap.get("pos3D").bufferManager.buffer, this._bufferManagersMap.get("pos2D").bufferManager.buffer, this._bufferManagersMap.get("rgba").bufferManager.buffer, (this._terrainFit && this._useElevationWithTerrainFit) ? this._bufferManagersMap.get("height").bufferManager.buffer : undefined);
125
+ }
126
+ setPointSize(size) {
127
+ this.pointSizes.pointSize = size;
128
+ this._drawOptionsUBO.update({ pointSize: size });
129
+ this.globe?.DrawRender();
130
+ }
131
+ setSelectionPointFilling(size) {
132
+ this.pointSizes.selectionPointFilling = size;
133
+ this._drawOptionsUBO.update({ selectionPointFilling: size });
134
+ this.globe?.DrawRender();
135
+ }
136
+ setHoveredPointSize(size) {
137
+ this.pointSizes.hoveredPointSize = size;
138
+ this._drawOptionsUBO.update({ hoveredPointSize: size });
139
+ this.globe?.DrawRender();
140
+ }
141
+ setOpacity(opacity) {
142
+ if (opacity < 0 || opacity > 1)
143
+ return;
144
+ this._opacity = opacity;
145
+ this._drawOptionsUBO.update({ opacity });
146
+ this.globe?.DrawRender();
147
+ }
148
+ getCurrentSelection() {
149
+ return this._selectedObj;
150
+ }
151
+ /**
152
+ *
153
+ * @param {*} x screen x
154
+ * @param {*} y screen y
155
+ * @param {*} callback callback on selection
156
+ */
157
+ screenSelection(x, y, callback) {
158
+ const { pointSizes, _pickerDisplayer } = this;
159
+ const objectStore = this._bufferManagersMap.get("objectStore").bufferManager;
160
+ const wrapper = (selectedIDsSet) => {
161
+ const selectedIDs = Array.from(selectedIDsSet);
162
+ if (selectedIDs.length === 0) {
163
+ this._selectedObj = null;
164
+ this._selectedID = -1;
165
+ callback([]);
166
+ return;
167
+ }
168
+ const selectedPoints = [];
169
+ for (let i = 0; i < selectedIDs.length; i++) {
170
+ const id = selectedIDs[i];
171
+ if (i === 0) {
172
+ this._selectedID = id;
173
+ this._selectedObj = objectStore.get(id);
174
+ }
175
+ const obj = objectStore.get(id);
176
+ selectedPoints.push(obj);
177
+ }
178
+ callback(selectedPoints);
179
+ };
180
+ _pickerDisplayer.pickXY(x, y, pointSizes.selectionPointFilling, wrapper);
181
+ }
182
+ /**
183
+ * @param {Array<Track>} tracks
184
+ * @returns
185
+ */
186
+ insertBulk(tracks) {
187
+ this._fillTracksToPointsMap(tracks);
188
+ const flattenedPoints = tracks.map(track => trackToFlatPoints(track, this._objectStoreExtraParametersFiltered)).flat();
189
+ const currentLoad = this._bufferOrchestrator.length;
190
+ if (currentLoad + flattenedPoints.length >= 2147483647) {
191
+ throw new Error("Too many points, Point count cannot exceed 2147483647");
192
+ }
193
+ const { _bufferManagersMap, _bufferOrchestrator } = this;
194
+ _bufferOrchestrator.insertBulk(flattenedPoints, _bufferManagersMap);
195
+ this._refillFocus();
196
+ this.globe?.DrawRender();
197
+ }
198
+ flush() {
199
+ const capacity = 100;
200
+ this._bufferOrchestrator?.flush({ capacity });
201
+ this._bufferManagersMap.forEach(({ bufferManager }) => bufferManager.resetWithCapacity(capacity));
202
+ this._turnOffFocus();
203
+ this._tracksToPointsMap.clear();
204
+ this.globe?.DrawRender();
205
+ }
206
+ /**
207
+ * @param {string} trackID
208
+ */
209
+ deleteTrack(trackID) {
210
+ const pointList = this._tracksToPointsMap.get(trackID);
211
+ if (!pointList) {
212
+ console.warn(`Track with ID ${trackID} does not exist.`);
213
+ return;
214
+ }
215
+ ;
216
+ const points = Array.from(pointList);
217
+ const { _bufferOrchestrator, _bufferManagersMap } = this;
218
+ _bufferOrchestrator.deleteBulk(points.map((pointID) => keyMethod(trackID, pointID)), _bufferManagersMap);
219
+ this._tracksToPointsMap.delete(trackID);
220
+ this._redraw = true;
221
+ this._refillFocus();
222
+ this.globe?.DrawRender();
223
+ }
224
+ /**
225
+ * @param {string} trackID
226
+ * @param {Array<string>} pointIDs
227
+ */
228
+ deletePoints(trackID, pointIDs) {
229
+ const { _bufferOrchestrator, _bufferManagersMap } = this;
230
+ _bufferOrchestrator.deleteBulk(pointIDs.map((pointID) => keyMethod(trackID, pointID)), _bufferManagersMap);
231
+ this._deletePointsFromTracksMap(trackID, pointIDs);
232
+ this._refillFocus();
233
+ this.globe?.DrawRender();
234
+ }
235
+ focusTracks(trackIDs = null) {
236
+ if (!this.globe)
237
+ return;
238
+ if (trackIDs === null) {
239
+ this._focusParams.on = false;
240
+ this.globe?.DrawRender();
241
+ return;
242
+ }
243
+ this._focusParams.on = true;
244
+ this._fillElementBuffer(trackIDs);
245
+ if (!this._focusParams.on) {
246
+ this._turnOffFocus();
247
+ }
248
+ this.globe.DrawRender();
249
+ }
250
+ updateTrackColor(trackID, rgba) {
251
+ if (!this._tracksToPointsMap.has(trackID)) {
252
+ console.warn(`Track with ID ${trackID} does not exist.`);
253
+ return;
254
+ }
255
+ const pointList = this._tracksToPointsMap.get(trackID);
256
+ const points = Array.from(pointList);
257
+ const { _bufferOrchestrator, _bufferManagersMap } = this;
258
+ _bufferOrchestrator.updateBulk(points.map((pointID) => {
259
+ return {
260
+ key: keyMethod(trackID, pointID),
261
+ rgba: rgba
262
+ };
263
+ }), _bufferManagersMap, ["rgba"]);
264
+ this._refillFocus();
265
+ this.globe?.DrawRender();
266
+ }
267
+ // GLOBE API METHODS
268
+ free() {
269
+ if (this._isFreed)
270
+ return;
271
+ this._isFreed = true;
272
+ this._pickerDisplayer.free();
273
+ PointOnGlobeProgramCache.release(this.globe, this._terrainFitMode);
274
+ this.gl.deleteBuffer(this._focusParams.elementBuffer);
275
+ this._bufferManagersMap.forEach(({ bufferManager, adaptor }) => bufferManager.free());
276
+ }
277
+ draw3D() {
278
+ const { gl, _pointProgram, _pickerDisplayer, _bufferOrchestrator, _vao } = this;
279
+ if (!gl) {
280
+ throw new Error("GL is not loaded, PointTracks Plugin");
281
+ }
282
+ if (this._isFreed) {
283
+ throw new Error("Plugin is unregistered, PointTracks Plugin");
284
+ }
285
+ this.resize();
286
+ _pickerDisplayer.bindFBO();
287
+ _pickerDisplayer.clearTextures();
288
+ if (this._focusParams.on) {
289
+ _pointProgram.draw(_vao, this._focusParams.length, this._drawOptionsUBO, this._focusParams.elementBuffer);
290
+ }
291
+ else {
292
+ _pointProgram.draw(_vao, _bufferOrchestrator.length, this._drawOptionsUBO, null);
293
+ }
294
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
295
+ _pickerDisplayer.drawColorTexture();
296
+ this._selfSelect();
297
+ }
298
+ resize() {
299
+ const w = this.globe.api_ScrW();
300
+ const h = this.globe.api_ScrH();
301
+ if (w === this._lastWH.w && h === this._lastWH.h)
302
+ return;
303
+ this._lastWH.w = w;
304
+ this._lastWH.h = h;
305
+ this._pickerDisplayer.resize();
306
+ this.globe?.DrawRender();
307
+ }
308
+ // IMPLICIT METHODS
309
+ _fillTracksToPointsMap(tracks) {
310
+ for (const track of tracks) {
311
+ const trackID = track.trackID;
312
+ const points = track.points;
313
+ if (!this._tracksToPointsMap.has(trackID)) {
314
+ this._tracksToPointsMap.set(trackID, new Set());
315
+ }
316
+ const pointSet = this._tracksToPointsMap.get(trackID);
317
+ for (let p = 0; p < points.length; p++) {
318
+ const pointID = points[p].ID;
319
+ if (!pointSet.has(pointID)) {
320
+ pointSet.add(pointID);
321
+ }
322
+ else {
323
+ console.warn(`Point with ID ${pointID} already exists in track ${trackID}. Skipping duplicate.`);
324
+ }
325
+ }
326
+ }
327
+ }
328
+ _refillFocus() {
329
+ if (this._focusParams.on) {
330
+ this.focusTracks(this._focusParams.trackIDs);
331
+ }
332
+ }
333
+ _deletePointsFromTracksMap(trackID, pointIDs) {
334
+ const trackIDs = this._tracksToPointsMap.get(trackID);
335
+ if (trackIDs === undefined) {
336
+ console.warn(`Track with ID ${trackID} not found.`);
337
+ return;
338
+ }
339
+ for (const pointID of pointIDs) {
340
+ if (!trackIDs.has(pointID)) {
341
+ console.warn(`Point with ID ${pointID} not found in track ${trackID}.`);
342
+ }
343
+ else {
344
+ trackIDs.delete(pointID);
345
+ }
346
+ }
347
+ if (trackIDs.size === 0) {
348
+ this._tracksToPointsMap.delete(trackID);
349
+ }
350
+ }
351
+ _selfSelect() {
352
+ const { globe } = this;
353
+ const pos = globe.api_GetMousePos();
354
+ const x = pos.canvasX;
355
+ const y = globe.api_ScrH() - pos.canvasY;
356
+ this.screenSelection(x, y, (selectedPoints) => {
357
+ if (selectedPoints.length > 0) {
358
+ if (this._lastSelectedID !== this._selectedID) {
359
+ this._lastSelectedID = this._selectedID;
360
+ this._drawOptionsUBO.updateSingle("hovered_vertexID", this._selectedID);
361
+ this.globe.DrawRender();
362
+ }
363
+ }
364
+ else {
365
+ if (this._lastSelectedID !== -1) {
366
+ this._lastSelectedID = -1;
367
+ this._drawOptionsUBO.updateSingle("hovered_vertexID", -1);
368
+ this.globe.DrawRender();
369
+ }
370
+ }
371
+ });
372
+ }
373
+ _turnOffFocus() {
374
+ const { gl } = this;
375
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._focusParams.elementBuffer);
376
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint32Array([]), gl.STATIC_DRAW);
377
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
378
+ this._focusParams.length = 0;
379
+ this._focusParams.on = false;
380
+ }
381
+ _fillElementBuffer(trackIDs) {
382
+ let length = 0;
383
+ const indexes = [];
384
+ const foundTracks = [];
385
+ for (const trackID of trackIDs) {
386
+ const pointList = this._tracksToPointsMap.get(trackID);
387
+ if (!pointList)
388
+ continue;
389
+ foundTracks.push(trackID);
390
+ for (const pointID of pointList) {
391
+ const key = keyMethod(trackID, pointID);
392
+ const index = this._bufferOrchestrator.offsetMap.get(key);
393
+ if (index === undefined) {
394
+ throw new Error(`Point with key ${key} not found in buffer orchestrator.`);
395
+ }
396
+ indexes.push(index);
397
+ length++;
398
+ }
399
+ }
400
+ this._focusParams.trackIDs = foundTracks;
401
+ this._focusParams.length = length;
402
+ if (length === 0) {
403
+ return false;
404
+ }
405
+ const { gl } = this;
406
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, this._focusParams.elementBuffer);
407
+ gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint32Array(indexes), gl.STATIC_DRAW);
408
+ gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
409
+ return true;
410
+ }
411
+ }
412
+ const trackToFlatPoints = (track, extraParameters) => {
413
+ const trackID = track.trackID;
414
+ const points = track.points;
415
+ const rgba = new Float32Array(track.rgba);
416
+ const flatPoints = [];
417
+ for (const point of points) {
418
+ flatPoints.push({
419
+ key: keyMethod(trackID, point.ID),
420
+ long: point.long,
421
+ lat: point.lat,
422
+ height: point.height,
423
+ pointID: point.ID,
424
+ rgba,
425
+ trackID
426
+ });
427
+ for (let i = 0; i < extraParameters.length; i++) {
428
+ const param = extraParameters[i];
429
+ if (point.hasOwnProperty(param)) {
430
+ flatPoints[flatPoints.length - 1][param] = point[param];
431
+ }
432
+ }
433
+ }
434
+ return flatPoints;
435
+ };
436
+ const keyMethod = (trackID, pointID) => `${trackID}_${pointID}`;
437
+ export { PointTracksPlugin, keyMethod };
@@ -1,7 +1,8 @@
1
1
  export class ObjectStore {
2
2
  _container;
3
- constructor({ initialCapcity = 10 } = {}) {
4
- this._container = this._createEmptyList(initialCapcity);
3
+ buffer = undefined; // For compatibility with BufferManager interface
4
+ constructor({ initialCapacity = 10 } = {}) {
5
+ this._container = this._createEmptyList(initialCapacity);
5
6
  }
6
7
  resetWithCapacity(capacity) {
7
8
  this._container = this._createEmptyList(capacity);