@pirireis/webglobeplugins 0.7.5 → 0.8.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
@@ -147,6 +147,31 @@ const wgs84ToUnitVector = (coordinates) => {
147
147
  return [x, y, z];
148
148
  }
149
149
 
150
+ /**
151
+ * @param {number} long wgs84
152
+ * @param {number} lat wgs84
153
+ * @param {number} height
154
+ * @returns {vec3} cartesian3D
155
+ */
156
+ const wgs84ToCartesian3d = (long, lat, height) => {
157
+ const longRad = long * RADIANS;
158
+ const latRad = lat * RADIANS;
159
+ const x = Math.cos(latRad) * Math.cos(longRad);
160
+ const y = Math.cos(latRad) * Math.sin(longRad);
161
+ const z = Math.sin(latRad);
162
+ const radius = WORLD_RADIUS_3D + height;
163
+ return [x * radius, y * radius, z * radius];
164
+ }
165
+
166
+ /**
167
+ * @param {number} long
168
+ * @param {number} lat
169
+ * @returns {vec2} mercator
170
+ */
171
+ const wgs84ToMercator = (long, lat) => {
172
+ return [WORLD_RADIUS_MERCATOR * long * RADIANS, WORLD_RADIUS_MERCATOR * Math.log(Math.tan(Math.PI / 4 + lat * RADIANS / 2))];
173
+ }
174
+
150
175
 
151
176
  /**
152
177
  * @param {vec3} cartesian
@@ -190,6 +215,8 @@ export {
190
215
  sphericalLinearInterpolation_Mercator,
191
216
  sphericalLinearInterpolation_Cartesian3d,
192
217
  wgs84ToUnitVector,
218
+ wgs84ToCartesian3d,
219
+ wgs84ToMercator,
193
220
  cartesian3dToUnitVectorWithHeight,
194
221
  pixelXYLenghtToUnitVectorWithHeight
195
222
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pirireis/webglobeplugins",
3
- "version": "0.7.5",
3
+ "version": "0.8.0",
4
4
  "main": "index.js",
5
5
  "author": "Toprak Nihat Deniz Ozturk",
6
6
  "license": "MIT"
@@ -1,6 +1,6 @@
1
1
  import { densityToLegendProgramCache } from "../programs/data2legend/density-to-legend";
2
2
  import { pointToDensityTextureCache } from "../programs/data2legend/point-to-density-texture";
3
- import { textureOnCanvasProgramCache } from "../util/programs/draw-texture-on-canvas";
3
+ // import { textureOnCanvasProgramCache } from "../util/programs/draw-texture-on-canvas";
4
4
  import { defaultblendfunction } from "../util/webglobe/gldefaultstates";
5
5
  class PointHeatmapFlow {
6
6
 
@@ -13,7 +13,7 @@ class PointHeatmapFlow {
13
13
  this.gl = globe.gl;
14
14
  this.pointToDensityProgram = pointToDensityTextureCache.get(globe);
15
15
  this.densityToLegendProgram = densityToLegendProgramCache.get(globe);
16
- // this.testTextureProgram = textureOnCanvasProgramCache.get(globe);
16
+ // this.testTextureProgram = textureOnCanvasProgramCache.get(globe.gl);
17
17
  this._lookInfo = globe.api_GetCurrentLookInfo();
18
18
  const { gl } = this;
19
19
  {
@@ -143,6 +143,7 @@ class PointHeatmapFlow {
143
143
  this._isFreed = true;
144
144
  densityToLegendProgramCache.release(this.globe);
145
145
  pointToDensityTextureCache.release(this.globe);
146
+ // textureOnCanvasProgramCache.release(this.gl);
146
147
  this.gl.deleteTexture(this.densityTexture);
147
148
  this.gl.deleteFramebuffer(this.framebuffer);
148
149
  this.gl.deleteBuffer(this.buffer);
@@ -1,20 +1,49 @@
1
- import { BufferOrchestrator, BufferManager } from "../util/account";
1
+ import { BufferOrchestrator, BufferManager, ObjectStore } from "../util/account";
2
2
 
3
+ import { PickerDisplayer } from "../util/picking/picker-displayer";
4
+ import { pointOnGlobeProgramCache } from "../programs/point-on-globe/square-pixel-point";
5
+
6
+ import { wgs84ToCartesian3d, wgs84ToMercator } from "../Math/methods";
7
+ import { hover } from "@testing-library/user-event/dist/hover";
3
8
 
4
9
  /**
5
10
  *
6
11
  * @typedef { long ,lat, height, ID } Point
7
- * @typedef {Array<Point>, rgba, TrackID} Track
12
+ * @typedef {Array<Point>, rgba, trackID} Track
8
13
  */
9
14
 
15
+ // TODO: redraw after insert and delete operations
16
+
10
17
  class PointTracksPlugin {
11
18
 
12
- constructor(id, { } = {}) {
19
+ constructor(id, { pointSize = 2, hoveredPointSize = 4, selectionPointFilling = 4, opacity = 1.0 } = {}) {
13
20
  this.id = id;
14
21
  this._isFreed = false;
15
- this._tracksInfoMap = new Map();
22
+
23
+ this._pointProgram = null
24
+ this._vao = null;
25
+ this._bufferManagersMap = null;
26
+ this._bufferOrchestrator = null;
27
+
28
+ this._pickerDisplayer = null
29
+ this._selectedID = -1;
30
+ this._selectedObj = null;
31
+
32
+ this._tracksToPointsMap = new Map(); // one to many
33
+
34
+ this._opacity = opacity;
35
+ this.pointSizes = {
36
+ pointSize,
37
+ selectionPointFilling,
38
+ hoveredPointSize
39
+ }
40
+
41
+
42
+ this._lastWH = { w: 0, h: 0 };
16
43
 
17
44
  this.program = null
45
+ // selectionPointFilling will be used to create a selection buffer and the iteration
46
+
18
47
  }
19
48
 
20
49
 
@@ -22,59 +51,264 @@ class PointTracksPlugin {
22
51
 
23
52
  this.globe = globe;
24
53
  this.gl = gl;
25
-
54
+ this._pickerDisplayer = new PickerDisplayer(globe);
55
+ this._pointProgram = pointOnGlobeProgramCache.get(globe);
26
56
  this._initBufferManagers();
27
57
  }
28
58
 
29
- _initBufferManagers() {
30
-
31
59
 
60
+ _initBufferManagers() {
32
61
  const { gl } = this;
33
62
  const initialCapacity = 10;
34
-
63
+ const bufferType = "DYNAMIC_DRAW";
35
64
  this._bufferOrchestrator = new BufferOrchestrator({ initialCapacity });
36
65
  this._bufferManagersMap = new Map(
37
66
  [
38
- ["pos3D", new BufferManager(gl, 3, { bufferType: gl.ARRAY_BUFFER, initialCapacity })],
39
- ["pos2D", new BufferManager(gl, 2, { bufferType: gl.ARRAY_BUFFER, initialCapacity })],
40
- ["rgba", new BufferManager(gl, 4, { bufferType: gl.ARRAY_BUFFER, initialCapacity })],
67
+ ["pos3D", {
68
+ bufferManager: new BufferManager(gl, 3, { bufferType, initialCapacity }),
69
+ adaptor: (item) => new Float32Array(wgs84ToCartesian3d(item.long, item.lat, item.height))
70
+ }],
71
+ ["pos2D", {
72
+ bufferManager: new BufferManager(gl, 2, { bufferType, initialCapacity }),
73
+ adaptor: (item) => new Float32Array(wgs84ToMercator(item.long, item.lat))
74
+ }],
75
+ ["rgba", {
76
+ bufferManager: new BufferManager(gl, 4, { bufferType, initialCapacity }),
77
+ adaptor: (item) => item.rgba
78
+ }],
79
+ ["objectStore", {
80
+ bufferManager: new ObjectStore({ initialCapacity }),
81
+ adaptor: (item) => {
82
+ return {
83
+ trackID: item.trackID, pointID: item.pointID
84
+ }
85
+ }
86
+ }]
41
87
  ]
42
88
  );
43
89
 
44
- this._vao = this.program.createVAO(
45
- this._bufferManagersMap.get("pos3D"),
46
- this._bufferManagersMap.get("pos2D"),
47
- this._bufferManagersMap.get("rgba"),
90
+ this._vao = this._pointProgram.createVAO(
91
+ this._bufferManagersMap.get("pos3D").bufferManager.buffer,
92
+ this._bufferManagersMap.get("pos2D").bufferManager.buffer,
93
+ this._bufferManagersMap.get("rgba").bufferManager.buffer,
48
94
  );
95
+ }
49
96
 
50
97
 
98
+ setPointSize(size) {
99
+ this.pointSizes.pointSize = size;
100
+ this.globe?.DrawRender();
51
101
  }
52
102
 
103
+ setSelectionPointFilling(size) {
104
+ this.pointSizes.selectionPointFilling = size;
105
+ this.globe?.DrawRender();
106
+ }
107
+
108
+ setHoveredPointSize(size) {
109
+ this.pointSizes.hoveredPointSize = size;
110
+ this.globe?.DrawRender();
111
+ }
112
+
113
+ setOpacity(opacity) {
114
+ if (opacity < 0 || opacity > 1) return;
115
+ this._opacity = opacity;
116
+ this.globe?.DrawRender();
117
+ }
118
+
119
+ getCurrentSelection() {
120
+ return this._selectedObj;
121
+ }
53
122
 
54
123
 
55
124
  /**
56
125
  *
126
+ * @param {*} x screen x
127
+ * @param {*} y screen y
128
+ * @param {*} callback callback on selection
129
+ */
130
+ screenSelection(x, y, callback) {
131
+ const { pointSizes, _pickerDisplayer } = this;
132
+ const objectStore = this._bufferManagersMap.get("objectStore").bufferManager;
133
+ const wrapper = (selectedIDsSet) => {
134
+ const selectedIDs = Array.from(selectedIDsSet);
135
+ if (selectedIDs.length === 0) {
136
+ this._selectedObj = null;
137
+ this._selectedID = -1;
138
+ callback([]);
139
+ return;
140
+ }
141
+ const selectedPoints = [];
142
+ for (let i = 0; i < selectedIDs.length; i++) {
143
+ const id = selectedIDs[i];
144
+ if (i === 0) {
145
+ this._selectedID = id;
146
+ this._selectedObj = objectStore.get(id);
147
+ }
148
+ const obj = objectStore.get(id);
149
+ selectedPoints.push(obj);
150
+ }
151
+
152
+ callback(selectedPoints);
153
+ }
154
+ _pickerDisplayer.pickXY(x, y, pointSizes.selectionPointFilling, wrapper);
155
+ }
156
+
157
+
158
+ /**
57
159
  * @param {Array<Track>} tracks
58
- *
59
160
  * @returns
60
161
  */
61
162
  insertBulk(tracks) {
62
- const { _bufferManagersMap, _bufferOrchestrator, _tracksInfoMap } = this;
163
+ this._fillTracksToPointsMap(tracks);
164
+ const flattenedPoints = tracks.map(trackToFlatPoints).flat();
165
+ const currentLoad = this._bufferOrchestrator.length;
166
+ if (currentLoad + flattenedPoints.length >= 2147483647) {
167
+ throw new Error("Too many points, Point count cannot exceed 2147483647");
168
+ }
169
+ const { _bufferManagersMap, _bufferOrchestrator } = this;
170
+ console.log("point tracks plugin insert bulk", flattenedPoints);
171
+ _bufferOrchestrator.insertBulk(flattenedPoints, _bufferManagersMap);
172
+ this.globe?.DrawRender();
173
+
174
+ }
175
+
63
176
 
64
- const { pos3DIndex, pos2DIndex, rgbaIndex } = _bufferOrchestrator.insertBulk(tracks);
65
- const trackID = tracks[0].trackID;
66
- _tracksInfoMap.set(trackID, { pos3DIndex, pos2DIndex, rgbaIndex });
67
- return trackID;
177
+ /**
178
+ * @param {string} trackID
179
+ */
180
+ deleteTrack(trackID) {
181
+ const pointSet = this._tracksToPointsMap.get(trackID);
182
+ const points = Array.from(pointSet);
183
+ const { _bufferOrchestrator, _bufferManagersMap } = this;
184
+ _bufferOrchestrator.deleteBulk(
185
+ points.map((pointID) => keyMethod(trackID, pointID)),
186
+ _bufferManagersMap
187
+ );
188
+ this._tracksToPointsMap.delete(trackID);
189
+ this._redraw = true;
190
+ this.globe?.DrawRender();
191
+ }
192
+
193
+
194
+ /**
195
+ * @param {string} trackID
196
+ * @param {Array<string>} pointIDs
197
+ */
198
+ deletePoints(trackID, pointIDs) {
199
+ const { _bufferOrchestrator, _bufferManagersMap } = this;
200
+ _bufferOrchestrator.deleteBulk(
201
+ pointIDs.map((pointID) => keyMethod(trackID, pointID)),
202
+ _bufferManagersMap
203
+ );
204
+ this._deletePointsFromTracksMap(trackID, pointIDs);
205
+ this.globe?.DrawRender();
68
206
  }
69
207
 
208
+ // GLOBE API METHODS
70
209
 
71
210
  free() {
72
211
  if (this._isFreed) return;
73
212
  this._isFreed = true;
213
+ this._pickerDisplayer.free();
214
+ pointOnGlobeProgramCache.release(this.globe);
215
+ this._bufferManagersMap.forEach(
216
+ ({ bufferManager, adaptor }) => bufferManager.free()
217
+ );
218
+ }
219
+
220
+
221
+ draw3D() {
222
+ const { gl, _pointProgram, _pickerDisplayer, _bufferOrchestrator, _vao } = this;
223
+ this.resize();
224
+
225
+ _pickerDisplayer.bindFBO();
226
+ _pickerDisplayer.clearTextures();
227
+ _pointProgram.draw(_vao, _bufferOrchestrator.length,
228
+ {
229
+ hoveredID: this._selectedID,
230
+ opacity: this._opacity,
231
+ pointSize: this.pointSizes.pointSize,
232
+ hoveredPointSize: this.pointSizes.hoveredPointSize
233
+
234
+ });
235
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
236
+
237
+ _pickerDisplayer.drawColorTexture();
238
+ this._selfSelect();
74
239
  }
75
240
 
76
241
 
242
+ resize() {
243
+ const w = this.globe.api_ScrW();
244
+ const h = this.globe.api_ScrH();
245
+ if (w === this._lastWH.w && h === this._lastWH.h) return;
246
+ console.log("point tracks plugin resize", w, h);
247
+
248
+ this._lastWH.w = w;
249
+ this._lastWH.h = h;
250
+ this._pickerDisplayer.resize();
251
+ this.globe?.DrawRender();
252
+ }
253
+
254
+ // IMPLICIT METHODS
255
+
256
+ _fillTracksToPointsMap(tracks) {
257
+ for (const track of tracks) {
258
+ const trackID = track.trackID;
259
+ const points = track.points;
260
+ if (!this._tracksToPointsMap.has(trackID)) {
261
+ this._tracksToPointsMap.set(trackID, new Set());
262
+ }
263
+ const pointSet = this._tracksToPointsMap.get(trackID);
264
+ for (let p = 0; p < points.length; p++) {
265
+ const pointID = points[p].ID;
266
+ pointSet.add(pointID);
267
+ }
268
+ }
269
+ }
270
+
271
+ _deletePointsFromTracksMap(trackID, pointIDs) {
272
+ const trackSet = this._tracksToPointsMap.get(trackID);
273
+ for (const pointID of pointIDs) {
274
+ trackSet.delete(pointID);
275
+ }
276
+ }
277
+
278
+
279
+ _selfSelect() {
280
+ const { globe } = this;
281
+ const pos = globe.api_GetMousePos();
282
+ const x = pos.canvasX;
283
+ const y = globe.api_ScrH() - pos.canvasY;
284
+ this.screenSelection(x, y, (selectedPoints) => {
285
+ });
286
+
287
+ }
288
+ }
289
+
290
+
291
+ const trackToFlatPoints = (track) => {
292
+
293
+ const trackID = track.trackID;
294
+ const points = track.points;
295
+ const rgba = new Float32Array(track.rgba);
296
+ const flatPoints = [];
297
+ for (const point of points) {
298
+ flatPoints.push({
299
+ key: keyMethod(trackID, point.ID),
300
+ long: point.long,
301
+ lat: point.lat,
302
+ height: point.height,
303
+ pointID: point.ID,
304
+ rgba,
305
+ trackID
306
+ });
307
+ }
308
+ return flatPoints;
309
+ }
77
310
 
311
+ const keyMethod = (trackID, pointID) => `${trackID}_${pointID}`;
78
312
 
79
313
 
80
- }
314
+ export { PointTracksPlugin, keyMethod }
@@ -62,7 +62,6 @@ in vec2 v_frame;
62
62
  out vec4 outColor;
63
63
  void main() {
64
64
  if ( v_frame.x < -POLE || v_frame.x > POLE || v_frame.y < -POLE || v_frame.y > POLE ){ discard; }
65
-
66
65
  outColor = v_color;
67
66
  }`;
68
67
 
@@ -11,14 +11,16 @@ ${cartesian3DToGLPosition}
11
11
  precision highp float;
12
12
  precision highp int;
13
13
 
14
- uniform int hovered_instanceID;
14
+ uniform int hovered_vertexID; // can be removed
15
15
 
16
16
  in vec3 pos3D;
17
17
  in vec2 pos2D;
18
18
  in vec4 rgba;
19
19
 
20
+ uniform float pointSize;
21
+ uniform float hoveredPointSize;
20
22
 
21
- flat out highp int vInstanceID;
23
+ flat out highp int vVertexID;
22
24
 
23
25
 
24
26
  out vec4 v_rgba;
@@ -31,29 +33,30 @@ void main() {
31
33
  else{
32
34
  gl_Position = mercatorXYToGLPosition(pos2D);
33
35
  }
34
- if (hovered_instanceID == gl_InstanceID) {
35
- gl_PointSize = 3.0;
36
+ if (hovered_vertexID == gl_VertexID) {
37
+ gl_PointSize = hoveredPointSize;
36
38
  } else {
37
- gl_PointSize = 1.0;
39
+ gl_PointSize = pointSize;
40
+ }
38
41
  v_rgba = rgba;
42
+ vVertexID = gl_VertexID;
39
43
  }`;
40
44
 
41
45
  const fs = `#version 300 es
42
46
  precision highp float;
43
47
 
44
- uniform opacity;
45
-
46
- flat in highp int vInstanceID;
48
+ uniform float opacity;
47
49
 
50
+ flat in highp int vVertexID;
48
51
  in vec4 v_rgba;
49
52
 
50
53
  layout(location = 0) out vec4 fragColor;
51
- layout(location = 1) out int instanceID;
54
+ layout(location = 1) out int vertexID;
52
55
 
53
56
  void main() {
57
+ vertexID = vVertexID;
54
58
  fragColor = v_rgba;
55
59
  fragColor.a *= opacity;
56
- instanceID = vInstanceID;
57
60
  }`;
58
61
 
59
62
 
@@ -68,8 +71,26 @@ class PointOnGlobeProgram {
68
71
 
69
72
  const { gl, program } = this;
70
73
 
74
+
71
75
  this.uniforms = {
72
76
  opacity: gl.getUniformLocation(program, "opacity"),
77
+ hovered_vertexID: gl.getUniformLocation(program, "hovered_vertexID"),
78
+ hoveredPointSize: gl.getUniformLocation(program, "hoveredPointSize"),
79
+ pointSize: gl.getUniformLocation(program, "pointSize"),
80
+ }
81
+
82
+ { // assign opacity
83
+ this._lastOpacity = 1.0;
84
+ this._lastPointSize = 2.0;
85
+ this._lastHoveredPointSize = 4.0;
86
+ this._lastHoveredID = -1;
87
+ const currentProgram = gl.getParameter(gl.CURRENT_PROGRAM);
88
+ gl.useProgram(program);
89
+ gl.uniform1f(this.uniforms.opacity, this._lastOpacity);
90
+ gl.uniform1f(this.uniforms.pointSize, this._lastPointSize);
91
+ gl.uniform1f(this.uniforms.hoveredPointSize, this._lastHoveredPointSize);
92
+ gl.uniform1i(this.uniforms.hovered_vertexID, this._lastHoveredID);
93
+ gl.useProgram(currentProgram);
73
94
  }
74
95
 
75
96
  { // assign attribute locations
@@ -113,23 +134,35 @@ class PointOnGlobeProgram {
113
134
  return vao;
114
135
  }
115
136
 
116
- draw(vao, length, { opacity = 1.0, frameBuffer = null } = {}) {
137
+
138
+ draw(vao, length, { opacity = 1.0, hoveredID = -1, pointSize = 2.0, hoveredPointSize = 4.0 } = {}) {
117
139
  if (length === 0 || opacity === 0) return;
118
140
  const { gl, program, uniforms } = this;
119
141
  gl.useProgram(program);
120
- gl.uniform1f(uniforms.opacity, opacity);
121
- gl.bindVertexArray(vao);
122
- if (frameBuffer) {
123
- gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer);
124
- gl.
125
- gl.drawArrays(gl.POINTS, 0, length);
142
+ if (this._lastOpacity !== opacity) {
143
+ gl.uniform1f(uniforms.opacity, opacity);
144
+ this._lastOpacity = opacity;
145
+ }
146
+ if (this._lastPointSize !== pointSize) {
147
+ gl.uniform1f(uniforms.pointSize, pointSize);
148
+ this._lastPointSize = pointSize;
149
+ }
150
+ if (this._lastHoveredPointSize !== hoveredPointSize) {
151
+ gl.uniform1f(uniforms.hoveredPointSize, hoveredPointSize);
152
+ this._lastHoveredPointSize = hoveredPointSize;
126
153
  }
154
+ if (this._lastHoveredID !== hoveredID) {
155
+ gl.uniform1i(uniforms.hovered_vertexID, hoveredID);
156
+ this._lastHoveredID = hoveredID;
157
+ }
158
+ gl.bindVertexArray(vao);
127
159
  this.cameraBlockTotem.bind(this.cameraBlockBingingPoint);
128
160
  gl.drawArrays(gl.POINTS, 0, length);
129
161
  this.cameraBlockTotem.unbind(this.cameraBlockBingingPoint);
130
162
  gl.bindVertexArray(null);
131
163
  }
132
164
 
165
+
133
166
  free() {
134
167
  if (this._isFreed) return;
135
168
  const { gl, globe } = this;
@@ -101,7 +101,8 @@ class RangeRings {
101
101
  this._ringAccount.updateCentersCoordinate(items);
102
102
  const { globe,
103
103
  bufferOrchestrator, bufferManagersCompMap,
104
- paddingBufferOrchestrator, bufferManagersCompMapPadding
104
+ paddingBufferOrchestrator,
105
+ bufferManagersCompMapPadding
105
106
  } = this;
106
107
  for (const { centerID } of items) {
107
108
  const datas = this.__reconstructCentralRings(centerID);
@@ -1,4 +1,4 @@
1
1
  import { BufferOrchestrator } from "./buffer-orchestrator";
2
2
  import { BufferManager } from "./buffer-manager";
3
-
4
- export { BufferOrchestrator, BufferManager };
3
+ import { ObjectStore } from "./object-store";
4
+ export { BufferOrchestrator, BufferManager, ObjectStore };
@@ -0,0 +1,62 @@
1
+
2
+ export class ObjectStore {
3
+ constructor({ initialCapcity = 10 } = {}) {
4
+ this._container = this._createEmptyList(initialCapcity);
5
+ }
6
+
7
+
8
+ resetWithCapacity(capacity) {
9
+ this._container = this._createEmptyList(capacity);
10
+ }
11
+
12
+
13
+ deleteBulk(offsets) {
14
+ for (let i = 0; i < offsets.length; i++) {
15
+ const offset = offsets[i];
16
+ if (offset !== undefined) {
17
+ this._container[offset] = null;
18
+ }
19
+ }
20
+ }
21
+
22
+
23
+ insertBulk(objects, offsets) {
24
+ for (let i = 0; i < objects.length; i++) {
25
+ const object = objects[i];
26
+ const offset = offsets[i];
27
+ if (offset !== undefined) {
28
+ this._container[offset] = object;
29
+ }
30
+ }
31
+ }
32
+
33
+ defrag(offsetValues, occupiedCapacity, newCapacity) {
34
+
35
+ const hold = this._createEmptyList(newCapacity);
36
+ for (let i = 0; i < offsetValues.length; i++) {
37
+ const offset = offsetValues[i];
38
+ if (offset !== undefined) {
39
+ hold[i] = this._container[offset];
40
+ }
41
+ }
42
+ this._container = hold;
43
+ }
44
+
45
+ extendBuffer(occupiedCapacity, newCapacity) {
46
+ const oldCapacity = this._container.length;
47
+ this._container = this._container.concat(this._createEmptyList(newCapacity - oldCapacity));
48
+ }
49
+
50
+ _createEmptyList(size) {
51
+ return new Array(size).fill(null);
52
+ }
53
+
54
+ get(index) {
55
+ return this._container[index];
56
+ }
57
+
58
+
59
+ free() {
60
+
61
+ }
62
+ }
@@ -1,8 +1,3 @@
1
- const PI_BY_180 = Math.PI / 180;
2
-
3
-
4
-
5
-
6
1
  export function latLongToPixelXY(latitude, longitude) {
7
2
  return {
8
3
  x: (longitude + 180) / 360,
@@ -10,16 +5,6 @@ export function latLongToPixelXY(latitude, longitude) {
10
5
  };
11
6
  }
12
7
 
13
-
14
- // export function latLongToRadiansTwist(latitude, longitude) {
15
-
16
- // return {
17
- // y: (90 - latitude) * PI_BY_180,
18
- // x: longitude * PI_BY_180
19
- // };
20
- // }
21
-
22
-
23
8
  /**
24
9
  * createBBoxMatrix( minX, maxX, minY, maxY)
25
10
  * use this matrix to transform a point from 0 to 1 range to the bounding box
@@ -41,4 +26,4 @@ export function latLongBboxtoPixelXYBbox(minX, minY, maxX, maxY) {
41
26
  }
42
27
 
43
28
 
44
- export { GeoDataFromTexture } from './geodatafromtexture';
29
+ // export { GeoDataFromTexture } from './geodatafromtexture';
@@ -0,0 +1,46 @@
1
+ const fence = (gl) => new Promise((resolve, reject) => {
2
+ // This will create a fence. If this follows a `readPixels()` call, then it
3
+ // will signal to WebGL that the read instruction should be asynchronous.
4
+ // This means JS can continue to work on other tasks while the read instruction
5
+ // completes.
6
+ const sync = gl.fenceSync(gl.SYNC_GPU_COMMANDS_COMPLETE, 0);
7
+ gl.flush();
8
+
9
+ // There are TWO possible ways to check on the status of a WebGLSync object.
10
+
11
+ // 1. This will check it using clientWaitSync()
12
+ const checkStatus_v1 = () => {
13
+ const status = gl.clientWaitSync(sync, 0, 0);
14
+
15
+ // There are four possible values for status:
16
+ // a. ALREADY_SIGNALED and CONDITION_SATISFIED (done)
17
+ // b. TIMEOUT_EXPIRED (check again later)
18
+ // c. WAIT_FAILED (there was an error)
19
+ if (status === gl.TIMEOUT_EXPIRED) {
20
+ setTimeout(checkStatus_v1, 1);
21
+ } else if (status === gl.ALREADY_SIGNALED || status === gl.CONDITION_SATISFIED) {
22
+ gl.deleteSync(sync);
23
+ resolve();
24
+ } else {
25
+ gl.deleteSync(sync);
26
+ reject(new Error('Fence did not resolve normally for some reason'));
27
+ }
28
+ };
29
+
30
+ // 2. This will check with getSyncParameter(s, gl.SYNC_STATUS)
31
+ const checkStatus_v2 = () => {
32
+ const status = gl.getSyncParameter(sync, gl.SYNC_STATUS);
33
+
34
+ // There are only two possible values for status: SIGNALED and UNSIGNALED
35
+ // Note that there is no `error` signal, so there is no reason to use `reject()` here
36
+ if (status === gl.SIGNALED) {
37
+ gl.deleteSync(sync);
38
+ resolve();
39
+ } else {
40
+ setTimeout(checkStatus_v2, 1);
41
+ }
42
+ };
43
+ setTimeout(checkStatus_v2, 1);
44
+ });
45
+
46
+ export { fence };
@@ -0,0 +1,177 @@
1
+ /**
2
+ * add implicit texture display program for color
3
+ * add fence on query return and return id.
4
+ */
5
+
6
+ import { textureOnCanvasProgramCache } from "../programs/draw-texture-on-canvas";
7
+ import { fence } from "./fence";
8
+
9
+ const ESCAPE_VALUE = -1;
10
+
11
+
12
+ class PickerDisplayer {
13
+ constructor(globe) {
14
+ this.globe = globe;
15
+ this.gl = globe.gl;
16
+ const gl = this.gl;
17
+ this.colorTexture = gl.createTexture(); // bind to color attachment 0
18
+ this.indexTexture = gl.createTexture(); // bind to color attachment 1
19
+ this.fbo = gl.createFramebuffer();
20
+
21
+ this._pbo = undefined;
22
+ this._pboSize = 0;
23
+ this.resize();
24
+
25
+ this.displayer = textureOnCanvasProgramCache.get(this.gl);
26
+
27
+ }
28
+
29
+ resize(width = null, height = null) {
30
+ if (width === null || height === null) {
31
+ width = this.globe.api_ScrW();
32
+ height = this.globe.api_ScrH();
33
+ }
34
+ console.log("resize picker displayer", width, height);
35
+ const { gl, colorTexture, indexTexture } = this;
36
+
37
+ gl.deleteTexture(colorTexture);
38
+ gl.deleteTexture(indexTexture);
39
+ const colorTextureNew = gl.createTexture();
40
+ gl.bindTexture(gl.TEXTURE_2D, colorTextureNew);
41
+ gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, width, height);
42
+ const indexTextureNew = gl.createTexture();
43
+ gl.bindTexture(gl.TEXTURE_2D, indexTextureNew);
44
+ // gl.texStorage2D(gl.TEXTURE_2D, 1, gl.R16I, width, height);
45
+ gl.texStorage2D(gl.TEXTURE_2D, 1, gl.R32I, width, height);
46
+ gl.bindTexture(gl.TEXTURE_2D, null);
47
+
48
+ this.colorTexture = colorTextureNew;
49
+ this.indexTexture = indexTextureNew;
50
+ }
51
+
52
+
53
+ clearTextures() {
54
+ const { gl, colorTexture, indexTexture } = this;
55
+ gl.activeTexture(gl.TEXTURE1);
56
+ gl.bindTexture(gl.TEXTURE_2D, indexTexture);
57
+ gl.clearBufferiv(gl.COLOR, 1, new Int32Array([-1, -1, -1, -1]));
58
+
59
+ gl.activeTexture(gl.TEXTURE0);
60
+ gl.bindTexture(gl.TEXTURE_2D, colorTexture);
61
+ gl.clearBufferfv(gl.COLOR, 0, new Float32Array([0, 0, 0, 0]));
62
+ }
63
+
64
+
65
+ // call before drawing the scene with gl picker shader
66
+ bindFBO() {
67
+ const { gl, colorTexture, indexTexture, fbo } = this;
68
+ gl.activeTexture(gl.TEXTURE1);
69
+ gl.bindTexture(gl.TEXTURE_2D, indexTexture);
70
+ gl.activeTexture(gl.TEXTURE0);
71
+ gl.bindTexture(gl.TEXTURE_2D, colorTexture);
72
+ { // bind framebuffer
73
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
74
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.colorTexture, 0);
75
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_2D, this.indexTexture, 0);
76
+ gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]);
77
+ }
78
+ }
79
+
80
+
81
+ // call after drawing the scene with gl picker shader
82
+ drawColorTexture() {
83
+ const { colorTexture } = this;
84
+ this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null);
85
+ this.displayer.draw(colorTexture);
86
+ }
87
+
88
+
89
+ pickXY(x, y, selectionPointFilling = 1, callback = () => { }) {
90
+ const size = Math.pow(1 + 2 * selectionPointFilling, 2);
91
+ this._pick(size,
92
+ x - selectionPointFilling,
93
+ y - selectionPointFilling,
94
+ 1 + 2 * selectionPointFilling,
95
+ 1 + 2 * selectionPointFilling,
96
+ callback);
97
+ }
98
+
99
+
100
+ pickBbox(left, top, right, bottom, callback) {
101
+ const size = (right - left) * (bottom - top) * 4;
102
+ this._pick(size, left, top, right - left, bottom - top, callback);
103
+ }
104
+
105
+
106
+ _pick(size, startX, startY, lengthX, lengthY, callback) {
107
+ if (this._inProgress) return false;
108
+ this._inProgress = true;
109
+
110
+ this._initHoldBuffer(size * 4);
111
+ const { gl, _pbo } = this;
112
+ gl.bindBuffer(gl.PIXEL_PACK_BUFFER, _pbo);
113
+ this.bindFBO();
114
+ gl.readBuffer(gl.COLOR_ATTACHMENT1); // This is the attachment we want to read
115
+ gl.readPixels( // This will read the pixels to the buffer asynchronously
116
+ startX, startY, lengthX, lengthY,
117
+ gl.RED_INTEGER, gl.INT,
118
+ 0
119
+ );
120
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
121
+ gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
122
+ fence(this.gl).then(() => {
123
+ gl.bindBuffer(gl.PIXEL_PACK_BUFFER, _pbo);
124
+ // const data = new Int16Array(size);
125
+ const data = new Int32Array(size);
126
+ gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, data);
127
+ gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
128
+ const result = this._pickFromBuffer(data, size);
129
+ callback(result);
130
+ // gl.deleteBuffer(pbo);
131
+ this._inProgress = false
132
+ }
133
+ );
134
+ return true;
135
+ }
136
+
137
+
138
+ _pickFromBuffer(array, size) {
139
+ const selectedObjects = new Set();
140
+ for (let i = 0; i < size; i += 1) {
141
+ const id = array[i];
142
+ if (id !== ESCAPE_VALUE) {
143
+ selectedObjects.add(id);
144
+ }
145
+ }
146
+ return selectedObjects;
147
+ }
148
+
149
+
150
+ _initHoldBuffer(size) {
151
+ if (this._pbo && this._pboSize >= size) {
152
+ return
153
+ }
154
+ const { gl } = this;
155
+ const pbo = gl.createBuffer();
156
+ gl.bindBuffer(gl.PIXEL_PACK_BUFFER, pbo);
157
+ gl.bufferData(gl.PIXEL_PACK_BUFFER, size, gl.STREAM_READ);
158
+ gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
159
+ this._pboSize = size;
160
+ if (this._pbo !== undefined) {
161
+ gl.deleteBuffer(this._pbo);
162
+ }
163
+ this._pbo = pbo;
164
+ }
165
+
166
+
167
+ free() {
168
+ const { gl, colorTexture, indexTexture, fbo } = this;
169
+ gl.deleteTexture(colorTexture);
170
+ gl.deleteTexture(indexTexture);
171
+ gl.deleteFramebuffer(fbo);
172
+ textureOnCanvasProgramCache.release(this.gl);
173
+ }
174
+ }
175
+
176
+
177
+ export { PickerDisplayer };
@@ -1,5 +1,5 @@
1
1
  import { createProgram } from "../webglobjectbuilders";
2
- import { noRegisterGlobeProgramCache } from "../../programs";
2
+ import { glProgramCache } from "../../programs";
3
3
  // import { CameraUniformBlockTotemCache, CameraUniformBlockString } from "../../programs/totems";
4
4
 
5
5
 
@@ -30,9 +30,8 @@ void main() {
30
30
 
31
31
 
32
32
  class TextureOnCanvasProgram {
33
- constructor(globe) {
34
- this.globe = globe;
35
- this.gl = globe.gl;
33
+ constructor(gl) {
34
+ this.gl = gl;
36
35
 
37
36
  this.vao = this.gl.createVertexArray();
38
37
  this.buffer = this.gl.createBuffer();
@@ -95,8 +94,8 @@ class TextureOnCanvasProgram {
95
94
  }
96
95
 
97
96
  const textureOnCanvasProgramCache = Object.freeze({
98
- get: (globe) => noRegisterGlobeProgramCache.getProgram(globe, TextureOnCanvasProgram),
99
- release: (program) => noRegisterGlobeProgramCache.releaseProgram(program)
97
+ get: (gl) => glProgramCache.getProgram(gl, TextureOnCanvasProgram),
98
+ release: (gl) => glProgramCache.releaseProgram(gl, TextureOnCanvasProgram)
100
99
  });
101
100
 
102
101
 
@@ -1,11 +1,26 @@
1
1
 
2
+ // export function createShader(gl, type, source) {
3
+ // const shader = gl.createShader(type);
4
+ // gl.shaderSource(shader, source);
5
+
6
+ // gl.compileShader(shader);
7
+ // if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
8
+ // throw new Error(gl.getShaderInfoLog(shader));
9
+ // }
10
+
11
+ // return shader;
12
+ // }
13
+
2
14
  export function createShader(gl, type, source) {
3
15
  const shader = gl.createShader(type);
4
16
  gl.shaderSource(shader, source);
5
17
 
6
18
  gl.compileShader(shader);
7
19
  if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
8
- throw new Error(gl.getShaderInfoLog(shader));
20
+ const errorLog = gl.getShaderInfoLog(shader);
21
+ const shaderType = type === gl.VERTEX_SHADER ? 'VERTEX_SHADER' : 'FRAGMENT_SHADER';
22
+ const sourceWithLineNumbers = source.split('\n').map((line, i) => `${i + 1}: ${line}`).join('\n');
23
+ throw new Error(`Error compiling ${shaderType}:\n${errorLog}\nSource:\n${sourceWithLineNumbers}`);
9
24
  }
10
25
 
11
26
  return shader;
@@ -1,52 +0,0 @@
1
- export class GeoDataFromTexture {
2
- /**
3
- * @param {Array} bbox - [minLong, minLat, maxLong, maxLat]
4
- * @param {Array} textureData - array of data
5
- * @param {Number} width - width of the texture
6
- * @param {Number} height - height of the texture
7
- * */
8
- constructor(bbox, textureData, width, height) {
9
- this.bbox = null;
10
- this.textureData = textureData;
11
- this.width = width;
12
- this.height = height;
13
- this.setBBox(bbox);
14
- }
15
-
16
- setBBox(bbox) {
17
- this.bbox = bbox;
18
- this._longRatio = (this.bbox[2] - this.bbox[0]) / this.width;
19
-
20
- this._latRatio = (this.bbox[3] - this.bbox[1]) / this.height;
21
- }
22
-
23
- setTextureData(textureData) {
24
- this.textureData = textureData;
25
- }
26
-
27
- getFloored(lat, long) {
28
- const x = (long - this.bbox[0]) / this._longRatio;
29
- const y = (lat - this.bbox[1]) / this._latRatio;
30
- //console.log(x + " = (" + long + " - " + this.bbox[0] + ") / " + this._longRatio);
31
- //console.log(y + " = (" + lat + " - " + this.bbox[1] + ") / " + this._latRatio);
32
- const index = Math.floor(y) * this.width + Math.floor(x);
33
- //console.log({ index, x, y, w: this.width, h: this.height, "total": this.width * this.height });
34
- return this.textureData[index];
35
- }
36
-
37
- getInterpolated(lat, long) {
38
- // this has bug for the last row and column
39
- const x = (long - this.bbox[0]) / (this.bbox[2] - this.bbox[0]);
40
- const y = (lat - this.bbox[1]) / (this.bbox[3] - this.bbox[1]);
41
- const xIndex = Math.floor(x * this.width);
42
- const yIndex = Math.floor(y * this.height);
43
- const xRatio = x * this.width - xIndex;
44
- const yRatio = y * this.height - yIndex;
45
- const index = yIndex * this.width + xIndex;
46
- const data = this.textureData[index];
47
- const data1 = this.textureData[index + 1];
48
- const data2 = this.textureData[index + this.width];
49
- const data3 = this.textureData[index + this.width + 1];
50
- return data * (1 - xRatio) * (1 - yRatio) + data1 * xRatio * (1 - yRatio) + data2 * (1 - xRatio) * yRatio + data3 * xRatio * yRatio;
51
- }
52
- }