@pirireis/webglobeplugins 1.0.7 → 1.0.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pirireis/webglobeplugins",
3
- "version": "1.0.7",
3
+ "version": "1.0.8",
4
4
  "main": "index.js",
5
5
  "author": "Toprak Nihat Deniz Ozturk",
6
6
  "license": "MIT",
@@ -212,7 +212,7 @@ export class TextureDemTriangles {
212
212
  if (this.globe.api_GetCurrentLODWithDecimal() < 14.5) {
213
213
  drawOnTopBegin(gl);
214
214
  }
215
- drawArrays(gl, gl.TRIANGLES, drawOptions);
215
+ drawArrays(gl, drawOptions.drawMode ?? gl.TRIANGLES, drawOptions);
216
216
  if (this.globe.api_GetCurrentLODWithDecimal() < 14.5) {
217
217
  drawOnTopEnd(gl);
218
218
  }
@@ -4,6 +4,7 @@ import { noRegisterGlobeProgramCache } from "../../../programs/programcache";
4
4
  import { PickerDisplayer } from "../../../util/picking/picker-displayer";
5
5
  import { IndexPolygonMap } from "./data/index-polygon-map";
6
6
  import { TestRecords } from "./test-records";
7
+ import { defaultblendfunction } from "../../../util/globe-default-gl-states";
7
8
  export class TerrainPolygonSemiPlugin {
8
9
  id;
9
10
  globe = null;
@@ -218,6 +219,8 @@ export class TerrainPolygonSemiPlugin {
218
219
  draw3D() {
219
220
  const gl = this.globe.gl;
220
221
  gl.disable(gl.DEPTH_TEST);
222
+ gl.enable(gl.BLEND);
223
+ defaultblendfunction(gl);
221
224
  // drawPoints
222
225
  if (this._options.showTesselationPoints) {
223
226
  this._program.draw(this._vao, this._drawPointsRangeIndexParams, this._uboHandler);
@@ -280,3 +283,23 @@ function inputCheck(input) {
280
283
  }
281
284
  return true;
282
285
  }
286
+ function getGlStates(gl) {
287
+ return {
288
+ depthTest: gl.isEnabled(gl.DEPTH_TEST),
289
+ cullFace: gl.isEnabled(gl.CULL_FACE),
290
+ blend: gl.isEnabled(gl.BLEND),
291
+ scissorTest: gl.isEnabled(gl.SCISSOR_TEST),
292
+ depthMask: gl.getParameter(gl.DEPTH_WRITEMASK),
293
+ colorMask: gl.getParameter(gl.COLOR_WRITEMASK),
294
+ frontFace: gl.getParameter(gl.FRONT_FACE),
295
+ viewport: gl.getParameter(gl.VIEWPORT),
296
+ scissorBox: gl.getParameter(gl.SCISSOR_BOX),
297
+ currentProgram: gl.getParameter(gl.CURRENT_PROGRAM),
298
+ vao: gl.getParameter(gl.VERTEX_ARRAY_BINDING),
299
+ arrayBuffer: gl.getParameter(gl.ARRAY_BUFFER_BINDING),
300
+ elementArrayBuffer: gl.getParameter(gl.ELEMENT_ARRAY_BUFFER_BINDING),
301
+ framebuffer: gl.getParameter(gl.FRAMEBUFFER_BINDING),
302
+ activeTexture: gl.getParameter(gl.ACTIVE_TEXTURE),
303
+ texture2DArray: gl.getParameter(gl.TEXTURE_BINDING_2D_ARRAY),
304
+ };
305
+ }
@@ -0,0 +1,176 @@
1
+ /**
2
+ * add implicit texture display program for color
3
+ * add fence on query return and return id.
4
+ */
5
+ import { textureOnCanvasProgramCache } from "../programs/draw-texture-on-canvas";
6
+ import { fence } from "./fence";
7
+ const ESCAPE_VALUE = -1;
8
+ class PickerDisplayer {
9
+ globe;
10
+ gl;
11
+ colorTexture;
12
+ indexTexture;
13
+ fbo;
14
+ _pbo;
15
+ _pboSize;
16
+ displayer;
17
+ _inProgress;
18
+ _indexType; // R32I for Integer, R32F for Float
19
+ _typedArrayConstructor;
20
+ constructor(globe, indexType = "R32I") {
21
+ this.globe = globe;
22
+ this.gl = globe.gl;
23
+ const gl = this.gl;
24
+ this.colorTexture = gl.createTexture(); // bind to color attachment 0
25
+ this.indexTexture = gl.createTexture(); // bind to color attachment 1
26
+ this.fbo = gl.createFramebuffer();
27
+ if (indexType === "R32F" && this.gl.getExtension("EXT_color_buffer_float") === null) {
28
+ throw new Error("EXT_color_buffer_float extension is required for R32F index type.");
29
+ }
30
+ this._indexType = indexType;
31
+ if (indexType === "R32I") {
32
+ this._typedArrayConstructor = Int32Array;
33
+ }
34
+ else if (indexType === "R32F") {
35
+ this._typedArrayConstructor = Float32Array;
36
+ }
37
+ else {
38
+ throw new Error("Invalid index type. Must be 'R32I' or 'R32F'.");
39
+ }
40
+ this._pbo = undefined;
41
+ this._pboSize = 0;
42
+ this._inProgress = false;
43
+ this.resize();
44
+ this.displayer = textureOnCanvasProgramCache.get(this.gl);
45
+ }
46
+ resize(width = null, height = null) {
47
+ if (width === null || height === null) {
48
+ width = this.globe.api_ScrW();
49
+ height = this.globe.api_ScrH();
50
+ }
51
+ const { gl, colorTexture, indexTexture } = this;
52
+ gl.deleteTexture(colorTexture);
53
+ gl.deleteTexture(indexTexture);
54
+ const colorTextureNew = gl.createTexture();
55
+ gl.activeTexture(gl.TEXTURE0);
56
+ gl.bindTexture(gl.TEXTURE_2D, colorTextureNew);
57
+ gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, width, height);
58
+ const indexTextureNew = gl.createTexture();
59
+ gl.activeTexture(gl.TEXTURE1);
60
+ gl.bindTexture(gl.TEXTURE_2D, indexTextureNew);
61
+ gl.texStorage2D(gl.TEXTURE_2D, 1, gl[this._indexType], width, height); // gl.R32I or gl.R32F
62
+ gl.bindTexture(gl.TEXTURE_2D, null);
63
+ this.colorTexture = colorTextureNew;
64
+ this.indexTexture = indexTextureNew;
65
+ }
66
+ clearTextures() {
67
+ const { gl, colorTexture, indexTexture } = this;
68
+ gl.activeTexture(gl.TEXTURE1);
69
+ gl.bindTexture(gl.TEXTURE_2D, indexTexture);
70
+ if (this._indexType === "R32I") {
71
+ gl.clearBufferiv(gl.COLOR, 1, new Int32Array([-1, -1, -1, -1]));
72
+ }
73
+ else {
74
+ gl.clearBufferfv(gl.COLOR, 1, new Float32Array([-1, -1, -1, -1]));
75
+ }
76
+ gl.activeTexture(gl.TEXTURE0);
77
+ gl.bindTexture(gl.TEXTURE_2D, colorTexture);
78
+ gl.clearBufferfv(gl.COLOR, 0, new Float32Array([0, 0, 0, 0]));
79
+ }
80
+ // call before drawing the scene with gl picker shader
81
+ bindFBO() {
82
+ const { gl, colorTexture, indexTexture, fbo } = this;
83
+ gl.activeTexture(gl.TEXTURE1);
84
+ gl.bindTexture(gl.TEXTURE_2D, indexTexture);
85
+ gl.activeTexture(gl.TEXTURE0);
86
+ gl.bindTexture(gl.TEXTURE_2D, colorTexture);
87
+ gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
88
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.colorTexture, 0);
89
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_2D, this.indexTexture, 0);
90
+ gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]);
91
+ const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER);
92
+ if (status !== gl.FRAMEBUFFER_COMPLETE) {
93
+ throw new Error(`Framebuffer is not complete: ${status.toString(16)}`);
94
+ }
95
+ }
96
+ // call after drawing the scene with gl picker shader
97
+ drawColorTexture() {
98
+ const { gl, colorTexture } = this;
99
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
100
+ gl.disable(gl.DEPTH_TEST);
101
+ this.displayer.draw(colorTexture);
102
+ gl.enable(gl.DEPTH_TEST);
103
+ }
104
+ pickXY(x, y, selectionPointFilling = 1, callback = () => { }) {
105
+ const size = Math.pow(1 + 2 * selectionPointFilling, 2);
106
+ return this._pick(size, x - selectionPointFilling, y - selectionPointFilling, 1 + 2 * selectionPointFilling, 1 + 2 * selectionPointFilling, callback);
107
+ }
108
+ pickBbox(left, top, right, bottom, callback) {
109
+ const size = (right - left) * (bottom - top);
110
+ return this._pick(size, left, top, right - left, bottom - top, callback);
111
+ }
112
+ _pick(size, startX, startY, lengthX, lengthY, callback) {
113
+ if (this._inProgress)
114
+ return false;
115
+ this._inProgress = true;
116
+ this._initHoldBuffer(size * 4);
117
+ const { gl, _pbo } = this;
118
+ gl.bindBuffer(gl.PIXEL_PACK_BUFFER, _pbo);
119
+ this.bindFBO();
120
+ gl.readBuffer(gl.COLOR_ATTACHMENT1); // This is the attachment we want to read
121
+ const format = this._indexType === "R32I" ? gl.RED_INTEGER : gl.RED;
122
+ const type = this._indexType === "R32I" ? gl.INT : gl.FLOAT;
123
+ gl.readPixels(// This will read the pixels to the buffer asynchronously
124
+ startX, startY, lengthX, lengthY, format, type, 0);
125
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
126
+ gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
127
+ fence(this.gl).then(() => {
128
+ gl.bindBuffer(gl.PIXEL_PACK_BUFFER, _pbo);
129
+ // const data = new Int16Array(size);
130
+ const data = new this._typedArrayConstructor(size); // Int32Array or Float32Array
131
+ gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, data);
132
+ gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
133
+ const result = this._pickFromBuffer(data, size);
134
+ callback(result);
135
+ // gl.deleteBuffer(pbo);
136
+ this._inProgress = false;
137
+ });
138
+ return true;
139
+ }
140
+ _pickFromBuffer(array, size) {
141
+ const selectedObjects = new Set();
142
+ for (let i = 0; i < size; i += 1) {
143
+ const id = array[i];
144
+ if (id !== ESCAPE_VALUE) {
145
+ selectedObjects.add(id);
146
+ }
147
+ }
148
+ return selectedObjects;
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
+ free() {
166
+ const { gl, colorTexture, indexTexture, fbo } = this;
167
+ gl.deleteTexture(colorTexture);
168
+ gl.deleteTexture(indexTexture);
169
+ gl.deleteFramebuffer(fbo);
170
+ if (this._pbo) {
171
+ gl.deleteBuffer(this._pbo);
172
+ }
173
+ textureOnCanvasProgramCache.release(this.gl);
174
+ }
175
+ }
176
+ export { PickerDisplayer };
@@ -43,48 +43,108 @@ class PickerDisplayer {
43
43
  this.resize();
44
44
  this.displayer = textureOnCanvasProgramCache.get(this.gl);
45
45
  }
46
+ _saveState() {
47
+ const gl = this.gl;
48
+ const framebuffer = gl.getParameter(gl.FRAMEBUFFER_BINDING);
49
+ const pixelPackBuffer = gl.getParameter(gl.PIXEL_PACK_BUFFER_BINDING);
50
+ const readBuffer = gl.getParameter(gl.READ_BUFFER);
51
+ const depthTestEnabled = gl.isEnabled(gl.DEPTH_TEST);
52
+ const activeTexture = gl.getParameter(gl.ACTIVE_TEXTURE);
53
+ gl.activeTexture(gl.TEXTURE0);
54
+ const tex2D_unit0 = gl.getParameter(gl.TEXTURE_BINDING_2D);
55
+ gl.activeTexture(gl.TEXTURE1);
56
+ const tex2D_unit1 = gl.getParameter(gl.TEXTURE_BINDING_2D);
57
+ // Restore active texture last
58
+ gl.activeTexture(activeTexture);
59
+ const drawBuffer0 = gl.getParameter(gl.DRAW_BUFFER0);
60
+ const drawBuffer1 = gl.getParameter(gl.DRAW_BUFFER1);
61
+ return {
62
+ framebuffer,
63
+ pixelPackBuffer,
64
+ readBuffer,
65
+ depthTestEnabled,
66
+ activeTexture,
67
+ tex2D_unit0,
68
+ tex2D_unit1,
69
+ drawBuffer0,
70
+ drawBuffer1,
71
+ };
72
+ }
73
+ _restoreState(s) {
74
+ const gl = this.gl;
75
+ gl.bindFramebuffer(gl.FRAMEBUFFER, s.framebuffer);
76
+ gl.bindBuffer(gl.PIXEL_PACK_BUFFER, s.pixelPackBuffer);
77
+ gl.readBuffer(s.readBuffer);
78
+ if (s.depthTestEnabled)
79
+ gl.enable(gl.DEPTH_TEST);
80
+ else
81
+ gl.disable(gl.DEPTH_TEST);
82
+ gl.activeTexture(gl.TEXTURE0);
83
+ gl.bindTexture(gl.TEXTURE_2D, s.tex2D_unit0);
84
+ gl.activeTexture(gl.TEXTURE1);
85
+ gl.bindTexture(gl.TEXTURE_2D, s.tex2D_unit1);
86
+ gl.activeTexture(s.activeTexture);
87
+ // drawBuffers restore: default framebuffer expects BACK/NONE (implementation details vary),
88
+ // so only restore the number of buffers that makes sense for the bound FB.
89
+ if (s.framebuffer === null) {
90
+ gl.drawBuffers([s.drawBuffer0]);
91
+ }
92
+ else {
93
+ gl.drawBuffers([s.drawBuffer0, s.drawBuffer1]);
94
+ }
95
+ }
46
96
  resize(width = null, height = null) {
47
97
  if (width === null || height === null) {
48
98
  width = this.globe.api_ScrW();
49
99
  height = this.globe.api_ScrH();
50
100
  }
51
- const { gl, colorTexture, indexTexture } = this;
52
- gl.deleteTexture(colorTexture);
53
- gl.deleteTexture(indexTexture);
54
- const colorTextureNew = gl.createTexture();
55
- gl.activeTexture(gl.TEXTURE0);
56
- gl.bindTexture(gl.TEXTURE_2D, colorTextureNew);
57
- gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, width, height);
58
- const indexTextureNew = gl.createTexture();
59
- gl.activeTexture(gl.TEXTURE1);
60
- gl.bindTexture(gl.TEXTURE_2D, indexTextureNew);
61
- gl.texStorage2D(gl.TEXTURE_2D, 1, gl[this._indexType], width, height); // gl.R32I or gl.R32F
62
- gl.bindTexture(gl.TEXTURE_2D, null);
63
- this.colorTexture = colorTextureNew;
64
- this.indexTexture = indexTextureNew;
101
+ const gl = this.gl;
102
+ const saved = this._saveState();
103
+ try {
104
+ const { colorTexture, indexTexture } = this;
105
+ gl.deleteTexture(colorTexture);
106
+ gl.deleteTexture(indexTexture);
107
+ const colorTextureNew = gl.createTexture();
108
+ gl.bindTexture(gl.TEXTURE_2D, colorTextureNew);
109
+ gl.texStorage2D(gl.TEXTURE_2D, 1, gl.RGBA8, width, height);
110
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
111
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
112
+ const indexTextureNew = gl.createTexture();
113
+ gl.bindTexture(gl.TEXTURE_2D, indexTextureNew);
114
+ gl.texStorage2D(gl.TEXTURE_2D, 1, gl[this._indexType], width, height); // gl.R32I or gl.R32F
115
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
116
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
117
+ gl.bindTexture(gl.TEXTURE_2D, null);
118
+ this.colorTexture = colorTextureNew;
119
+ this.indexTexture = indexTextureNew;
120
+ }
121
+ finally {
122
+ this._restoreState(saved);
123
+ }
65
124
  }
66
125
  clearTextures() {
67
- const { gl, colorTexture, indexTexture } = this;
68
- gl.activeTexture(gl.TEXTURE1);
69
- gl.bindTexture(gl.TEXTURE_2D, indexTexture);
70
- if (this._indexType === "R32I") {
71
- gl.clearBufferiv(gl.COLOR, 1, new Int32Array([-1, -1, -1, -1]));
126
+ const gl = this.gl;
127
+ const saved = this._saveState();
128
+ try {
129
+ // Must clear the *FBO attachments*, not "textures".
130
+ this.bindFBO();
131
+ if (this._indexType === "R32I") {
132
+ gl.clearBufferiv(gl.COLOR, 1, new Int32Array([-1, -1, -1, -1]));
133
+ }
134
+ else {
135
+ gl.clearBufferfv(gl.COLOR, 1, new Float32Array([-1, -1, -1, -1]));
136
+ }
137
+ gl.clearBufferfv(gl.COLOR, 0, new Float32Array([0, 0, 0, 0]));
72
138
  }
73
- else {
74
- gl.clearBufferfv(gl.COLOR, 1, new Float32Array([-1, -1, -1, -1]));
139
+ finally {
140
+ this._restoreState(saved);
75
141
  }
76
- gl.activeTexture(gl.TEXTURE0);
77
- gl.bindTexture(gl.TEXTURE_2D, colorTexture);
78
- gl.clearBufferfv(gl.COLOR, 0, new Float32Array([0, 0, 0, 0]));
79
142
  }
80
143
  // call before drawing the scene with gl picker shader
81
144
  bindFBO() {
82
- const { gl, colorTexture, indexTexture, fbo } = this;
83
- gl.activeTexture(gl.TEXTURE1);
84
- gl.bindTexture(gl.TEXTURE_2D, indexTexture);
85
- gl.activeTexture(gl.TEXTURE0);
86
- gl.bindTexture(gl.TEXTURE_2D, colorTexture);
145
+ const { gl, fbo } = this;
87
146
  gl.bindFramebuffer(gl.FRAMEBUFFER, fbo);
147
+ // No need to bind textures or change ACTIVE_TEXTURE for framebufferTexture2D.
88
148
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.colorTexture, 0);
89
149
  gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT1, gl.TEXTURE_2D, this.indexTexture, 0);
90
150
  gl.drawBuffers([gl.COLOR_ATTACHMENT0, gl.COLOR_ATTACHMENT1]);
@@ -95,11 +155,16 @@ class PickerDisplayer {
95
155
  }
96
156
  // call after drawing the scene with gl picker shader
97
157
  drawColorTexture() {
98
- const { gl, colorTexture } = this;
99
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
100
- gl.disable(gl.DEPTH_TEST);
101
- this.displayer.draw(colorTexture);
102
- gl.enable(gl.DEPTH_TEST);
158
+ const gl = this.gl;
159
+ const saved = this._saveState();
160
+ try {
161
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
162
+ gl.disable(gl.DEPTH_TEST);
163
+ this.displayer.draw(this.colorTexture);
164
+ }
165
+ finally {
166
+ this._restoreState(saved);
167
+ }
103
168
  }
104
169
  pickXY(x, y, selectionPointFilling = 1, callback = () => { }) {
105
170
  const size = Math.pow(1 + 2 * selectionPointFilling, 2);
@@ -113,29 +178,44 @@ class PickerDisplayer {
113
178
  if (this._inProgress)
114
179
  return false;
115
180
  this._inProgress = true;
116
- this._initHoldBuffer(size * 4);
117
- const { gl, _pbo } = this;
118
- gl.bindBuffer(gl.PIXEL_PACK_BUFFER, _pbo);
119
- this.bindFBO();
120
- gl.readBuffer(gl.COLOR_ATTACHMENT1); // This is the attachment we want to read
121
- const format = this._indexType === "R32I" ? gl.RED_INTEGER : gl.RED;
122
- const type = this._indexType === "R32I" ? gl.INT : gl.FLOAT;
123
- gl.readPixels(// This will read the pixels to the buffer asynchronously
124
- startX, startY, lengthX, lengthY, format, type, 0);
125
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
126
- gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
127
- fence(this.gl).then(() => {
181
+ const gl = this.gl;
182
+ const saved = this._saveState();
183
+ try {
184
+ this._initHoldBuffer(size * 4);
185
+ const { _pbo } = this;
128
186
  gl.bindBuffer(gl.PIXEL_PACK_BUFFER, _pbo);
129
- // const data = new Int16Array(size);
130
- const data = new this._typedArrayConstructor(size); // Int32Array or Float32Array
131
- gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, data);
132
- gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
133
- const result = this._pickFromBuffer(data, size);
134
- callback(result);
135
- // gl.deleteBuffer(pbo);
187
+ this.bindFBO();
188
+ gl.readBuffer(gl.COLOR_ATTACHMENT1);
189
+ const format = this._indexType === "R32I" ? gl.RED_INTEGER : gl.RED;
190
+ const type = this._indexType === "R32I" ? gl.INT : gl.FLOAT;
191
+ gl.readPixels(startX, startY, lengthX, lengthY, format, type, 0);
192
+ // Restore state immediately after issuing readPixels (PBO keeps data for later getBufferSubData)
193
+ this._restoreState(saved);
194
+ fence(this.gl)
195
+ .then(() => {
196
+ gl.bindBuffer(gl.PIXEL_PACK_BUFFER, _pbo);
197
+ const data = new this._typedArrayConstructor(size);
198
+ gl.getBufferSubData(gl.PIXEL_PACK_BUFFER, 0, data);
199
+ gl.bindBuffer(gl.PIXEL_PACK_BUFFER, null);
200
+ const result = this._pickFromBuffer(data, size);
201
+ callback(result);
202
+ })
203
+ .catch(() => {
204
+ // Swallow or rethrow based on your app needs; important is not to wedge _inProgress.
205
+ })
206
+ .finally(() => {
207
+ this._inProgress = false;
208
+ });
209
+ return true;
210
+ }
211
+ catch {
212
+ this._restoreState(saved);
136
213
  this._inProgress = false;
137
- });
138
- return true;
214
+ throw new Error("Picking failed.");
215
+ }
216
+ finally {
217
+ this._restoreState(saved);
218
+ }
139
219
  }
140
220
  _pickFromBuffer(array, size) {
141
221
  const selectedObjects = new Set();