@pirireis/webglobeplugins 0.7.5 → 0.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/Math/methods.js CHANGED
@@ -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.1",
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,43 @@
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";
3
7
 
4
8
  /**
5
- *
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
6
15
  * @typedef { long ,lat, height, ID } Point
7
- * @typedef {Array<Point>, rgba, TrackID} Track
16
+ * @typedef {Array<Point>, rgba, trackID} Track
8
17
  */
9
18
 
19
+
10
20
  class PointTracksPlugin {
11
21
 
12
- constructor(id, { } = {}) {
22
+ constructor(id, { pointSize = 2, hoveredPointSize = 4, selectionPointFilling = 4, opacity = 1.0 } = {}) {
13
23
  this.id = id;
14
24
  this._isFreed = false;
15
- this._tracksInfoMap = new Map();
16
-
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
+ this._selectedObj = null;
32
+ this._tracksToPointsMap = new Map(); // one to many
33
+ this._opacity = opacity;
17
34
  this.program = null
35
+ this._lastWH = { w: 0, h: 0 };
36
+ this.pointSizes = {
37
+ pointSize,
38
+ selectionPointFilling,
39
+ hoveredPointSize
40
+ }
18
41
  }
19
42
 
20
43
 
@@ -22,59 +45,265 @@ class PointTracksPlugin {
22
45
 
23
46
  this.globe = globe;
24
47
  this.gl = gl;
25
-
48
+ this._pickerDisplayer = new PickerDisplayer(globe);
49
+ this._pointProgram = pointOnGlobeProgramCache.get(globe);
26
50
  this._initBufferManagers();
27
51
  }
28
52
 
29
- _initBufferManagers() {
30
-
31
53
 
54
+ _initBufferManagers() {
32
55
  const { gl } = this;
33
56
  const initialCapacity = 10;
34
-
57
+ const bufferType = "DYNAMIC_DRAW";
35
58
  this._bufferOrchestrator = new BufferOrchestrator({ initialCapacity });
36
59
  this._bufferManagersMap = new Map(
37
60
  [
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 })],
61
+ ["pos3D", {
62
+ bufferManager: new BufferManager(gl, 3, { bufferType, initialCapacity }),
63
+ adaptor: (item) => new Float32Array(wgs84ToCartesian3d(item.long, item.lat, item.height))
64
+ }],
65
+ ["pos2D", {
66
+ bufferManager: new BufferManager(gl, 2, { bufferType, initialCapacity }),
67
+ adaptor: (item) => new Float32Array(wgs84ToMercator(item.long, item.lat))
68
+ }],
69
+ ["rgba", {
70
+ bufferManager: new BufferManager(gl, 4, { bufferType, initialCapacity }),
71
+ adaptor: (item) => item.rgba
72
+ }],
73
+ ["objectStore", {
74
+ bufferManager: new ObjectStore({ initialCapacity }),
75
+ adaptor: (item) => {
76
+ return {
77
+ trackID: item.trackID, pointID: item.pointID
78
+ }
79
+ }
80
+ }]
41
81
  ]
42
82
  );
43
83
 
44
- this._vao = this.program.createVAO(
45
- this._bufferManagersMap.get("pos3D"),
46
- this._bufferManagersMap.get("pos2D"),
47
- this._bufferManagersMap.get("rgba"),
84
+ this._vao = this._pointProgram.createVAO(
85
+ this._bufferManagersMap.get("pos3D").bufferManager.buffer,
86
+ this._bufferManagersMap.get("pos2D").bufferManager.buffer,
87
+ this._bufferManagersMap.get("rgba").bufferManager.buffer,
48
88
  );
89
+ }
90
+
49
91
 
92
+ setPointSize(size) {
93
+ this.pointSizes.pointSize = size;
94
+ this.globe?.DrawRender();
95
+ }
50
96
 
97
+ setSelectionPointFilling(size) {
98
+ this.pointSizes.selectionPointFilling = size;
99
+ this.globe?.DrawRender();
51
100
  }
52
101
 
102
+ setHoveredPointSize(size) {
103
+ this.pointSizes.hoveredPointSize = size;
104
+ this.globe?.DrawRender();
105
+ }
106
+
107
+ setOpacity(opacity) {
108
+ if (opacity < 0 || opacity > 1) return;
109
+ this._opacity = opacity;
110
+ this.globe?.DrawRender();
111
+ }
112
+
113
+ getCurrentSelection() {
114
+ return this._selectedObj;
115
+ }
53
116
 
54
117
 
55
118
  /**
56
119
  *
120
+ * @param {*} x screen x
121
+ * @param {*} y screen y
122
+ * @param {*} callback callback on selection
123
+ */
124
+ screenSelection(x, y, callback) {
125
+ const { pointSizes, _pickerDisplayer } = this;
126
+ const objectStore = this._bufferManagersMap.get("objectStore").bufferManager;
127
+ const wrapper = (selectedIDsSet) => {
128
+ const selectedIDs = Array.from(selectedIDsSet);
129
+ if (selectedIDs.length === 0) {
130
+ this._selectedObj = null;
131
+ this._selectedID = -1;
132
+ callback([]);
133
+ return;
134
+ }
135
+ const selectedPoints = [];
136
+ for (let i = 0; i < selectedIDs.length; i++) {
137
+ const id = selectedIDs[i];
138
+ if (i === 0) {
139
+ this._selectedID = id;
140
+ this._selectedObj = objectStore.get(id);
141
+ }
142
+ const obj = objectStore.get(id);
143
+ selectedPoints.push(obj);
144
+ }
145
+
146
+ callback(selectedPoints);
147
+ }
148
+ _pickerDisplayer.pickXY(x, y, pointSizes.selectionPointFilling, wrapper);
149
+ }
150
+
151
+
152
+ /**
57
153
  * @param {Array<Track>} tracks
58
- *
59
154
  * @returns
60
155
  */
61
156
  insertBulk(tracks) {
62
- const { _bufferManagersMap, _bufferOrchestrator, _tracksInfoMap } = this;
157
+ this._fillTracksToPointsMap(tracks);
158
+ const flattenedPoints = tracks.map(trackToFlatPoints).flat();
159
+ const currentLoad = this._bufferOrchestrator.length;
160
+ if (currentLoad + flattenedPoints.length >= 2147483647) {
161
+ throw new Error("Too many points, Point count cannot exceed 2147483647");
162
+ }
163
+ const { _bufferManagersMap, _bufferOrchestrator } = this;
164
+ console.log("point tracks plugin insert bulk", flattenedPoints);
165
+ _bufferOrchestrator.insertBulk(flattenedPoints, _bufferManagersMap);
166
+ this.globe?.DrawRender();
63
167
 
64
- const { pos3DIndex, pos2DIndex, rgbaIndex } = _bufferOrchestrator.insertBulk(tracks);
65
- const trackID = tracks[0].trackID;
66
- _tracksInfoMap.set(trackID, { pos3DIndex, pos2DIndex, rgbaIndex });
67
- return trackID;
68
168
  }
69
169
 
70
170
 
171
+ /**
172
+ * @param {string} trackID
173
+ */
174
+ deleteTrack(trackID) {
175
+ const pointSet = this._tracksToPointsMap.get(trackID);
176
+ const points = Array.from(pointSet);
177
+ const { _bufferOrchestrator, _bufferManagersMap } = this;
178
+ _bufferOrchestrator.deleteBulk(
179
+ points.map((pointID) => keyMethod(trackID, pointID)),
180
+ _bufferManagersMap
181
+ );
182
+ this._tracksToPointsMap.delete(trackID);
183
+ this._redraw = true;
184
+ this.globe?.DrawRender();
185
+ }
186
+
187
+
188
+ /**
189
+ * @param {string} trackID
190
+ * @param {Array<string>} pointIDs
191
+ */
192
+ deletePoints(trackID, pointIDs) {
193
+ const { _bufferOrchestrator, _bufferManagersMap } = this;
194
+ _bufferOrchestrator.deleteBulk(
195
+ pointIDs.map((pointID) => keyMethod(trackID, pointID)),
196
+ _bufferManagersMap
197
+ );
198
+ this._deletePointsFromTracksMap(trackID, pointIDs);
199
+ this.globe?.DrawRender();
200
+ }
201
+
202
+ // GLOBE API METHODS
203
+
71
204
  free() {
72
205
  if (this._isFreed) return;
73
206
  this._isFreed = true;
207
+ this._pickerDisplayer.free();
208
+ pointOnGlobeProgramCache.release(this.globe);
209
+ this._bufferManagersMap.forEach(
210
+ ({ bufferManager, adaptor }) => bufferManager.free()
211
+ );
212
+ }
213
+
214
+
215
+ draw3D() {
216
+ const { gl, _pointProgram, _pickerDisplayer, _bufferOrchestrator, _vao } = this;
217
+ this.resize();
218
+
219
+ _pickerDisplayer.bindFBO();
220
+ _pickerDisplayer.clearTextures();
221
+ _pointProgram.draw(_vao, _bufferOrchestrator.length,
222
+ {
223
+ hoveredID: this._selectedID,
224
+ opacity: this._opacity,
225
+ pointSize: this.pointSizes.pointSize,
226
+ hoveredPointSize: this.pointSizes.hoveredPointSize
227
+
228
+ });
229
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
230
+
231
+ _pickerDisplayer.drawColorTexture();
232
+ this._selfSelect();
233
+ }
234
+
235
+
236
+ resize() {
237
+ const w = this.globe.api_ScrW();
238
+ const h = this.globe.api_ScrH();
239
+ if (w === this._lastWH.w && h === this._lastWH.h) return;
240
+ console.log("point tracks plugin resize", w, h);
241
+
242
+ this._lastWH.w = w;
243
+ this._lastWH.h = h;
244
+ this._pickerDisplayer.resize();
245
+ this.globe?.DrawRender();
246
+ }
247
+
248
+ // IMPLICIT METHODS
249
+
250
+ _fillTracksToPointsMap(tracks) {
251
+ for (const track of tracks) {
252
+ const trackID = track.trackID;
253
+ const points = track.points;
254
+ if (!this._tracksToPointsMap.has(trackID)) {
255
+ this._tracksToPointsMap.set(trackID, new Set());
256
+ }
257
+ const pointSet = this._tracksToPointsMap.get(trackID);
258
+ for (let p = 0; p < points.length; p++) {
259
+ const pointID = points[p].ID;
260
+ pointSet.add(pointID);
261
+ }
262
+ }
263
+ }
264
+
265
+
266
+ _deletePointsFromTracksMap(trackID, pointIDs) {
267
+ const trackSet = this._tracksToPointsMap.get(trackID);
268
+ for (const pointID of pointIDs) {
269
+ trackSet.delete(pointID);
270
+ }
74
271
  }
75
272
 
76
273
 
274
+ _selfSelect() {
275
+ const { globe } = this;
276
+ const pos = globe.api_GetMousePos();
277
+ const x = pos.canvasX;
278
+ const y = globe.api_ScrH() - pos.canvasY;
279
+ this.screenSelection(x, y, (selectedPoints) => {
280
+ });
281
+
282
+ }
283
+ }
284
+
285
+
286
+ const trackToFlatPoints = (track) => {
287
+
288
+ const trackID = track.trackID;
289
+ const points = track.points;
290
+ const rgba = new Float32Array(track.rgba);
291
+ const flatPoints = [];
292
+ for (const point of points) {
293
+ flatPoints.push({
294
+ key: keyMethod(trackID, point.ID),
295
+ long: point.long,
296
+ lat: point.lat,
297
+ height: point.height,
298
+ pointID: point.ID,
299
+ rgba,
300
+ trackID
301
+ });
302
+ }
303
+ return flatPoints;
304
+ }
77
305
 
306
+ const keyMethod = (trackID, pointID) => `${trackID}_${pointID}`;
78
307
 
79
308
 
80
- }
309
+ 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
- }