@pirireis/webglobeplugins 0.17.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/Math/haversine.js +22 -0
  2. package/Math/methods.js +15 -2
  3. package/Math/tessellation/methods.js +4 -1
  4. package/Math/tessellation/nearest-value-padding.js +112 -0
  5. package/Math/tessellation/spherical-triangle-area.js +99 -0
  6. package/Math/tessellation/tile-merger.js +346 -215
  7. package/Math/tessellation/triangle-tessellation.js +381 -9
  8. package/Math/vec3.js +4 -0
  9. package/Math/xyz-tile.js +18 -0
  10. package/altitude-locator/plugin.js +1 -2
  11. package/investigation-tools/draw/tiles/adapters.js +2 -2
  12. package/investigation-tools/draw/tiles/tiles.js +2 -2
  13. package/package.json +1 -1
  14. package/programs/helpers/fadeaway.js +6 -1
  15. package/programs/point-on-globe/square-pixel-point.js +1 -0
  16. package/programs/polygon-on-globe/texture-dem-triangles.js +94 -116
  17. package/programs/totems/camera-totem-attactment-interface.js +1 -0
  18. package/programs/totems/camerauniformblock.js +33 -22
  19. package/programs/totems/dem-textures-manager.js +265 -0
  20. package/programs/vectorfields/logics/drawrectangleparticles.js +51 -18
  21. package/programs/vectorfields/logics/{ubo-new.js → particle-ubo.js} +5 -14
  22. package/programs/vectorfields/logics/pixelbased.js +42 -36
  23. package/programs/vectorfields/pingpongbuffermanager.js +34 -8
  24. package/semiplugins/shape-on-terrain/terrain-polygon/adapters.js +55 -0
  25. package/semiplugins/shape-on-terrain/terrain-polygon/data/cache.js +102 -0
  26. package/semiplugins/shape-on-terrain/terrain-polygon/data/index-polygon-map.js +45 -0
  27. package/semiplugins/shape-on-terrain/terrain-polygon/data/manager.js +4 -0
  28. package/semiplugins/shape-on-terrain/terrain-polygon/data/master-worker.js +177 -0
  29. package/semiplugins/shape-on-terrain/terrain-polygon/data/polygon-to-triangles.js +100 -0
  30. package/semiplugins/shape-on-terrain/terrain-polygon/data/random.js +121 -0
  31. package/semiplugins/shape-on-terrain/terrain-polygon/data/types.js +1 -0
  32. package/semiplugins/shape-on-terrain/terrain-polygon/data/worker-contact.js +63 -0
  33. package/semiplugins/shape-on-terrain/terrain-polygon/data/worker.js +125 -0
  34. package/semiplugins/shape-on-terrain/terrain-polygon/terrain-polygon.js +219 -0
  35. package/semiplugins/shape-on-terrain/terrain-polygon/types.js +8 -0
  36. package/semiplugins/shell/bbox-renderer/logic.js +18 -58
  37. package/semiplugins/shell/bbox-renderer/object.js +19 -9
  38. package/tracks/point-heat-map/point-to-heat-map-flow.js +1 -1
  39. package/tracks/point-tracks/plugin.js +13 -6
  40. package/tracks/timetracks/program-line-strip.js +1 -1
  41. package/util/account/single-attribute-buffer-management/buffer-manager.js +5 -3
  42. package/util/account/single-attribute-buffer-management/buffer-orchestrator.js +2 -2
  43. package/util/gl-util/uniform-block/manager.js +20 -10
  44. package/util/helper-methods.js +8 -0
  45. package/util/picking/fence.js +4 -2
  46. package/util/picking/picker-displayer.js +51 -9
  47. package/util/programs/draw-texture-on-canvas.js +18 -15
  48. package/util/shaderfunctions/geometrytransformations.js +67 -1
  49. package/vectorfield/waveparticles/plugin.js +241 -116
  50. package/vectorfield/wind/adapters/image-to-fields.js +61 -0
  51. package/vectorfield/wind/adapters/types.js +1 -0
  52. package/vectorfield/wind/imagetovectorfieldandmagnitude.js +6 -9
  53. package/vectorfield/wind/plugin-persistant copy.js +364 -0
  54. package/vectorfield/wind/plugin-persistant.js +375 -0
  55. package/vectorfield/wind/plugin.js +1 -1
  56. package/Math/tessellation/earcut/adapters.js +0 -37
  57. package/Math/tessellation/hybrid-triangle-tessellation-meta.js +0 -123
  58. package/Math/tessellation/shred-input.js +0 -18
  59. package/Math/tessellation/tiler.js +0 -50
  60. package/Math/tessellation/triangle-tessellation-meta.js +0 -523
  61. package/programs/polygon-on-globe/texture-dem-triangle-test-plugin-triangle.js +0 -328
  62. package/programs/vectorfields/logics/drawrectangleparticles1.js +0 -112
  63. package/semiplugins/shape-on-terrain/terrain-cover/texture-dem-cover.js +0 -1
  64. package/util/gl-util/uniform-block/types.js +0 -1
  65. package/util/webglobe/index.js +0 -2
  66. /package/Math/tessellation/{zoom-catch.js → constants.js} +0 -0
  67. /package/util/{webglobe/gldefaultstates.js → globe-default-gl-states.js} +0 -0
@@ -1,48 +1,84 @@
1
- import { BBOXGlobeShell } from "../../semiplugins/shell/bbox-renderer";
2
- import { defaultblendfunction } from "../../util/webglobe/gldefaultstates";
1
+ import { BBOXGlobeShell } from "../../semiplugins/shell/bbox-renderer/index";
2
+ import { defaultblendfunction } from "../../util/globe-default-gl-states";
3
3
  import { PingPongBufferManager } from "../../programs/vectorfields/pingpongbuffermanager";
4
4
  import { pixelBasedMoveProgramCache } from "../../programs/vectorfields/logics/pixelbased";
5
5
  import { drawRectangleParticlesProgramCache } from "../../programs/vectorfields/logics/drawrectangleparticles";
6
6
  import { FadeAwayProgramCache } from "../../programs/helpers/fadeaway";
7
- import { SeaWaveUbo } from "../../programs/vectorfields/logics/ubo-new";
7
+ import { ParticleUBO } from "../../programs/vectorfields/logics/particle-ubo";
8
8
  /**
9
9
  * STEPS:
10
10
  * 1. move particle | buffers: read b1 write b2 | swap buffers b1 <-> b2
11
11
  * 2. draw particles | read b1 to drawTexture1 |
12
12
  * 3. globe shell drawTexture1
13
13
  * 4. fade drawTexture1 to drawTexture2 | swap textures
14
- *
15
14
  */
16
15
  const MAX_PIXELS_ON_DIMENSION = 2200;
17
16
  export default class Plugin {
18
- constructor(id, { dataWidth, dataHeight, fadeOpacity = 0.83, opacity = 0.75, minLon = -180, minLat = -90, maxLon = 180, maxLat = 90, patricleCount = 8000, flipY = true, drawTextureMaxPixelOnDimension = MAX_PIXELS_ON_DIMENSION } = {}) {
17
+ id;
18
+ options;
19
+ globe;
20
+ gl;
21
+ moveParticle;
22
+ drawParticle;
23
+ bufferManager;
24
+ _rgVectorFieldTexture;
25
+ globeShellWiggle;
26
+ waveUbo;
27
+ fadeAway;
28
+ _drawTextureResolution;
29
+ _frameBuffer;
30
+ _globeshellparameters;
31
+ _stepIndex;
32
+ _fullCycleStepCount;
33
+ _isFreed;
34
+ _drawTextures;
35
+ ____drawIndex;
36
+ __data = null;
37
+ _colorFieldTexture = null;
38
+ _initialColorFieldData = null;
39
+ _speedFieldTexture = null;
40
+ _initialSpeedFieldData = null;
41
+ constructor(id, options) {
19
42
  this.id = id;
20
- this.globe = null;
21
- this.gl = null;
22
- this.moveParticle = null;
23
- this.drawParticle = null;
24
- this.bufferManager = null;
25
- this._rgVectorFieldTexture = null;
26
- this.globeShellWiggle = null;
27
- this.waveUbo = null;
28
- this._flipY = flipY;
43
+ this.options = {
44
+ tailSize: options.tailSize || 1.0,
45
+ wingSize: options.wingSize || 7.0,
46
+ dataWidth: options.dataWidth || 256,
47
+ dataHeight: options.dataHeight || 256,
48
+ fadeOpacity: options.fadeOpacity ?? 0.83,
49
+ opacity: options.opacity ?? 0.75,
50
+ speed: options.speed ?? 0.6,
51
+ dropRate: options.dropRate ?? 0.007,
52
+ height: options.height ?? 0,
53
+ minLon: options.minLon ?? -180,
54
+ minLat: options.minLat ?? -90,
55
+ maxLon: options.maxLon ?? 180,
56
+ maxLat: options.maxLat ?? 90,
57
+ particleCount: options.particleCount ?? 8000,
58
+ flipY: options.flipY ?? true,
59
+ drawTextureMaxPixelOnDimension: options.drawTextureMaxPixelOnDimension ?? MAX_PIXELS_ON_DIMENSION,
60
+ useColorTexture: options.useColorTexture ?? false,
61
+ initialColorData: options.initialColorData ?? null,
62
+ useSpeedTexture: options.useSpeedTexture ?? false,
63
+ initialSpeedTexture: options.initialSpeedTexture ?? null,
64
+ minSpeedThreshold: options.minSpeedThreshold ?? 0.0,
65
+ maxSpeedThreshold: options.maxSpeedThreshold ?? 150.9,
66
+ };
67
+ console.log("WaveParticle options:", this.options);
29
68
  this._drawTextureResolution = {};
30
- this._frameBuffer = null;
31
- this._fadeOpacity = fadeOpacity;
32
- this._opacity = opacity;
33
- this._particleCount = patricleCount;
34
- this._dataWidth = dataWidth;
35
- this._dataHeight = dataHeight;
36
- this._drawTextureMaxPixelOnDimension = drawTextureMaxPixelOnDimension;
37
69
  this._globeshellparameters = {
38
- minLon,
39
- maxLon,
40
- minLat,
41
- maxLat,
70
+ minLon: this.options.minLon,
71
+ maxLon: this.options.maxLon,
72
+ minLat: this.options.minLat,
73
+ maxLat: this.options.maxLat,
42
74
  };
43
75
  this._stepIndex = 0;
44
76
  this._fullCycleStepCount = 3;
45
77
  this._isFreed = false;
78
+ this._drawTextures = [];
79
+ this.____drawIndex = 0;
80
+ this._initialColorFieldData = this.options.initialColorData ?? null;
81
+ this._initialSpeedFieldData = this.options.initialSpeedTexture ?? null;
46
82
  }
47
83
  init(globe, gl) {
48
84
  this.globe = globe;
@@ -52,55 +88,75 @@ export default class Plugin {
52
88
  this.fadeAway = FadeAwayProgramCache.get(gl);
53
89
  this.globeShellWiggle = new BBOXGlobeShell(gl, globe, this._globeshellparameters);
54
90
  const inPositionLocation = this.moveParticle.getInPositionLocation();
55
- this.bufferManager = new PingPongBufferManager(gl, this._particleCount, inPositionLocation);
91
+ this.bufferManager = new PingPongBufferManager(gl, this.options.particleCount, inPositionLocation);
56
92
  this._rgVectorFieldTexture = this._createRGTexture();
57
- this.waveUbo = SeaWaveUbo.createUBO(gl);
93
+ this.waveUbo = ParticleUBO.createUBO(gl);
94
+ if (this.options.useColorTexture) {
95
+ this.waveUbo.updateSingle("use_color_field", new Float32Array([1]));
96
+ if (this._initialColorFieldData) {
97
+ this.setColorField(this._initialColorFieldData);
98
+ this._initialColorFieldData = null;
99
+ }
100
+ else {
101
+ console.warn("useColorTexture is true but initialColorData is null");
102
+ }
103
+ }
104
+ if (this.options.useSpeedTexture) {
105
+ this.waveUbo.updateSingle("use_speed_field", new Float32Array([1]));
106
+ console.log("use speed texture");
107
+ if (this._initialSpeedFieldData) {
108
+ this.setSpeedField(this._initialSpeedFieldData);
109
+ this._initialSpeedFieldData = null;
110
+ }
111
+ else {
112
+ console.warn("useSpeedTexture is true but initialSpeedTexture is null");
113
+ }
114
+ }
58
115
  this._drawTextures = [this._createDrawTexture(), this._createDrawTexture()];
59
116
  this._frameBuffer = gl.createFramebuffer();
60
117
  this.setBBox(this._globeshellparameters);
61
- this.setOpacity(this._opacity);
118
+ this.setOpacity(this.options.opacity);
119
+ this.setParticleDimensions(this.options.tailSize, this.options.wingSize);
120
+ this.setParticleSpeed(this.options.speed);
121
+ this.setDropRate(this.options.dropRate);
122
+ this.setHeight(this.options.height);
123
+ // ensure speed thresholds are applied to UBO on init
124
+ this.setSpeedThresholds(this.options.minSpeedThreshold, this.options.maxSpeedThreshold);
62
125
  this.____drawIndex = 0;
63
126
  }
64
127
  draw3D() {
65
128
  if (this._isFreed)
66
129
  return;
67
130
  const { gl, moveParticle, drawParticle, bufferManager, globeShellWiggle, waveUbo, fadeAway, _rgVectorFieldTexture } = this;
131
+ if (!gl || !bufferManager || !globeShellWiggle || !waveUbo || !fadeAway || !_rgVectorFieldTexture)
132
+ return;
68
133
  gl.disable(gl.DEPTH_TEST);
69
- if (this._stepIndex === 0) {
70
- // waveUbo.update({ random_seed: Math.random() });
71
- this.waveUbo.updateSingle("random_seed", new Float32Array([Math.random()]));
72
- ;
73
- moveParticle.move(bufferManager, _rgVectorFieldTexture, waveUbo);
74
- // 1s swap buffer b1 <-> b2
75
- bufferManager.swap();
76
- }
77
- else if (this._stepIndex === 1) {
78
- this.____drawIndex = 1 - this.____drawIndex;
79
- const canvasWidth = gl.canvas.width;
80
- const canvasHeight = gl.canvas.height;
81
- // gl.viewport(0, 0, 2200, 2200);
82
- gl.viewport(0, 0, this._drawTextureResolution.width, this._drawTextureResolution.height);
83
- gl.bindFramebuffer(gl.FRAMEBUFFER, this._frameBuffer);
84
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this._drawTextures[this.____drawIndex], 0);
85
- drawParticle.draw(bufferManager, _rgVectorFieldTexture, waveUbo);
86
- // 2s bound frame buffer to null
87
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
88
- gl.viewport(0, 0, canvasWidth, canvasHeight);
89
- }
90
- else {
91
- const canvasWidth = gl.canvas.width;
92
- const canvasHeight = gl.canvas.height;
93
- // gl.viewport(0, 0, 2200, 2200);
94
- gl.viewport(0, 0, this._drawTextureResolution.width, this._drawTextureResolution.height);
95
- gl.bindFramebuffer(gl.FRAMEBUFFER, this._frameBuffer);
96
- gl.enable(gl.BLEND);
97
- // gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
98
- gl.blendFunc(gl.ONE, gl.ZERO);
99
- gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this._drawTextures[1 - this.____drawIndex], 0);
100
- fadeAway.draw(this._drawTextures[this.____drawIndex], this._fadeOpacity);
101
- gl.bindFramebuffer(gl.FRAMEBUFFER, null);
102
- defaultblendfunction(gl);
103
- gl.viewport(0, 0, canvasWidth, canvasHeight);
134
+ switch (this._stepIndex) {
135
+ case 0:
136
+ this.waveUbo.updateSingle("random_seed", new Float32Array([Math.random()]));
137
+ moveParticle.move(bufferManager, _rgVectorFieldTexture, waveUbo);
138
+ bufferManager.swap();
139
+ break;
140
+ case 1:
141
+ this.____drawIndex = 1 - this.____drawIndex;
142
+ gl.viewport(0, 0, this._drawTextureResolution.width, this._drawTextureResolution.height);
143
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this._frameBuffer);
144
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this._drawTextures[this.____drawIndex], 0);
145
+ drawParticle.draw(bufferManager, _rgVectorFieldTexture, waveUbo, this._colorFieldTexture, this._speedFieldTexture);
146
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
147
+ gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
148
+ break;
149
+ case 2:
150
+ gl.viewport(0, 0, this._drawTextureResolution.width, this._drawTextureResolution.height);
151
+ gl.bindFramebuffer(gl.FRAMEBUFFER, this._frameBuffer);
152
+ gl.enable(gl.BLEND);
153
+ gl.blendFunc(gl.ONE, gl.ZERO);
154
+ gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this._drawTextures[1 - this.____drawIndex], 0);
155
+ fadeAway.draw(this._drawTextures[this.____drawIndex], this.options.fadeOpacity);
156
+ gl.bindFramebuffer(gl.FRAMEBUFFER, null);
157
+ defaultblendfunction(gl);
158
+ gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
159
+ break;
104
160
  }
105
161
  globeShellWiggle.setTexture(this._drawTextures[this.____drawIndex]);
106
162
  globeShellWiggle.draw();
@@ -113,62 +169,68 @@ export default class Plugin {
113
169
  }
114
170
  this._fullCycleStepCount = value;
115
171
  }
116
- // TODO: free all resources
117
172
  free() {
118
173
  if (this._isFreed) {
119
174
  console.warn("WaveParticle plugin is already freed");
120
175
  return;
121
176
  }
122
- ;
123
177
  this._isFreed = true;
124
178
  const { gl, globeShellWiggle, _rgVectorFieldTexture, bufferManager, waveUbo, _drawTextures, _frameBuffer } = this;
125
- this.fadeAway = null;
179
+ if (!gl)
180
+ return;
126
181
  FadeAwayProgramCache.release(gl);
127
- globeShellWiggle.free();
128
- gl.deleteTexture(_rgVectorFieldTexture);
129
- // glProgramCache.releaseProgram(gl, MoveParticle);
130
- pixelBasedMoveProgramCache.getProgram(gl);
131
- this.moveParticle = null;
132
- // glProgramCache.releaseProgram(gl, DrawParticle);
133
- drawRectangleParticlesProgramCache.getProgram(gl);
134
- this.drawParticle = null;
135
- bufferManager.free();
136
- waveUbo.free();
182
+ globeShellWiggle?.free();
183
+ if (_rgVectorFieldTexture)
184
+ gl.deleteTexture(_rgVectorFieldTexture);
185
+ pixelBasedMoveProgramCache.releaseProgram(gl);
186
+ drawRectangleParticlesProgramCache.releaseProgram(gl);
187
+ bufferManager?.free();
188
+ waveUbo?.free();
137
189
  _drawTextures.forEach(texture => gl.deleteTexture(texture));
138
- gl.deleteFramebuffer(_frameBuffer);
190
+ if (_frameBuffer)
191
+ gl.deleteFramebuffer(_frameBuffer);
192
+ if (this._colorFieldTexture) {
193
+ gl.deleteTexture(this._colorFieldTexture);
194
+ this._colorFieldTexture = null;
195
+ }
196
+ if (this._speedFieldTexture) {
197
+ gl.deleteTexture(this._speedFieldTexture);
198
+ this._speedFieldTexture = null;
199
+ }
139
200
  }
140
- // setters
141
201
  setParticleCount(value) {
142
- this._particleCount = value;
143
- this.bufferManager.setParticleCount(value);
202
+ this.options.particleCount = value;
203
+ this.bufferManager?.setParticleCount(value);
144
204
  }
145
205
  setVectorFieldData(data, { dataWidth = null, dataHeight = null, flipY } = {}) {
146
206
  if (dataWidth !== null && dataHeight !== null) {
147
- this._dataWidth = dataWidth;
148
- this._dataHeight = dataHeight;
207
+ this.options.dataWidth = dataWidth;
208
+ this.options.dataHeight = dataHeight;
149
209
  }
150
210
  if (flipY !== undefined) {
151
- this._flipY = flipY;
211
+ this.options.flipY = flipY;
152
212
  }
153
213
  this.__data = data;
154
- const { gl, _dataWidth, _dataHeight } = this;
214
+ const { gl } = this;
215
+ if (!gl || !this._rgVectorFieldTexture)
216
+ return;
217
+ console.log("flipY:", this.options.flipY);
155
218
  gl.bindTexture(gl.TEXTURE_2D, this._rgVectorFieldTexture);
156
- gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, this._flipY);
157
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RG32F, _dataWidth, _dataHeight, 0, gl.RG, gl.FLOAT, data);
219
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, this.options.flipY);
220
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RG32F, this.options.dataWidth, this.options.dataHeight, 0, gl.RG, gl.FLOAT, data);
158
221
  gl.bindTexture(gl.TEXTURE_2D, null);
159
222
  gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false);
160
223
  }
161
224
  setHeight(value) {
162
- this.globeShellWiggle.setHeight(value);
225
+ this.globeShellWiggle?.setHeight(value);
163
226
  }
164
227
  setDropRate(value) {
165
- this.waveUbo.updateSingle("drop_rate", new Float32Array([value]));
166
- ;
228
+ this.waveUbo?.updateSingle("drop_rate", new Float32Array([value]));
167
229
  }
168
230
  setDrawTextureMaxPixelOnDimension(value) {
169
- this._drawTextureMaxPixelOnDimension = value;
231
+ this.options.drawTextureMaxPixelOnDimension = value;
170
232
  this._drawTextureResolution = this._drawTextureSizeFromBbox(this._globeshellparameters);
171
- this.waveUbo.updateSingle("draw_texture_size", new Float32Array([this._drawTextureResolution.width, this._drawTextureResolution.height]));
233
+ this.waveUbo?.updateSingle("draw_texture_size", new Float32Array([this._drawTextureResolution.width, this._drawTextureResolution.height]));
172
234
  this._drawTextures = [this._createDrawTexture(), this._createDrawTexture()];
173
235
  }
174
236
  setParticleSpeed(value) {
@@ -179,26 +241,27 @@ export default class Plugin {
179
241
  if (value > 100) {
180
242
  console.warn("Particle speed is too high, it may cause performance issues");
181
243
  }
182
- this.waveUbo.updateSingle("range", new Float32Array([value]));
244
+ this.waveUbo?.updateSingle("range", new Float32Array([value]));
183
245
  }
184
246
  setFadeOpacity(value) {
185
247
  if (typeof value !== 'number' || value < 0 || value > 1) {
186
248
  console.error("Fade opacity must be a number between 0 and 1");
187
249
  return;
188
250
  }
189
- this._fadeOpacity = value;
251
+ this.options.fadeOpacity = value;
190
252
  }
191
253
  setOpacity(value) {
192
254
  if (typeof value !== 'number' || value < 0 || value > 1) {
193
255
  console.error("Opacity must be a number between 0 and 1");
194
256
  return;
195
257
  }
196
- this.globeShellWiggle.setOpacity(value);
258
+ this.globeShellWiggle?.setOpacity(value);
197
259
  }
198
260
  setParticleDimensions(tail, wing) {
199
261
  if (0 < tail && 0 < wing) {
200
- this.waveUbo.updateSingle("tail_wing_base_limp", new Float32Array([tail, wing]));
201
- ;
262
+ this.options.tailSize = tail;
263
+ this.options.wingSize = wing;
264
+ this.waveUbo?.updateSingle("tail_wing_base_limp", new Float32Array([tail, wing]));
202
265
  }
203
266
  else {
204
267
  console.error("tail and wing must be positive numbers");
@@ -210,28 +273,88 @@ export default class Plugin {
210
273
  console.error("color must be an array of rgb elements");
211
274
  return;
212
275
  }
213
- this.waveUbo.updateSingle("color", new Float32Array(color));
214
- ;
276
+ this.waveUbo?.updateSingle("color", new Float32Array(color));
277
+ }
278
+ setColorField(data) {
279
+ const { gl } = this;
280
+ if (!gl)
281
+ return;
282
+ if (this.options.useColorTexture === false) {
283
+ console.warn("Color texture is not enabled.");
284
+ return;
285
+ }
286
+ if (!this._colorFieldTexture) {
287
+ this._colorFieldTexture = gl.createTexture();
288
+ }
289
+ if (data.length !== this.options.dataWidth * this.options.dataHeight * 4) {
290
+ console.error("Color field data length does not match the specified dataWidth and dataHeight.");
291
+ return;
292
+ }
293
+ gl.bindTexture(gl.TEXTURE_2D, this._colorFieldTexture);
294
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, this.options.flipY);
295
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this.options.dataWidth, this.options.dataHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, data);
296
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
297
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
298
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
299
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
300
+ gl.bindTexture(gl.TEXTURE_2D, null);
301
+ }
302
+ setSpeedField(data) {
303
+ const { gl } = this;
304
+ if (!gl)
305
+ return;
306
+ if (!this.options.useSpeedTexture) {
307
+ console.warn("Speed texture is not enabled.");
308
+ return;
309
+ }
310
+ if (!this._speedFieldTexture) {
311
+ this._speedFieldTexture = gl.createTexture();
312
+ }
313
+ if (data.length !== this.options.dataWidth * this.options.dataHeight) {
314
+ console.error("Speed field data length does not match the specified dataWidth and dataHeight.");
315
+ return;
316
+ }
317
+ console.log(data);
318
+ gl.bindTexture(gl.TEXTURE_2D, this._speedFieldTexture);
319
+ gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, this.options.flipY);
320
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.R32F, this.options.dataWidth, this.options.dataHeight, 0, gl.RED, gl.FLOAT, data);
321
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
322
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
323
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
324
+ gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
325
+ gl.bindTexture(gl.TEXTURE_2D, null);
326
+ }
327
+ /**
328
+ * Update speed thresholds used by shaders to cull particles by magnitude
329
+ */
330
+ setMinSpeedThreshold(value) {
331
+ this.options.minSpeedThreshold = value;
332
+ this.waveUbo?.updateSingle("min_speed_threshold", new Float32Array([value]));
333
+ }
334
+ setMaxSpeedThreshold(value) {
335
+ this.options.maxSpeedThreshold = value;
336
+ this.waveUbo?.updateSingle("max_speed_threshold", new Float32Array([value]));
337
+ }
338
+ setSpeedThresholds(min, max) {
339
+ this.setMinSpeedThreshold(min);
340
+ this.setMaxSpeedThreshold(max);
215
341
  }
216
342
  setBBox({ minLon, minLat, maxLon, maxLat }) {
217
343
  this._globeshellparameters = { minLon, minLat, maxLon, maxLat };
218
- this.globeShellWiggle.setBBox({ minLon, minLat, maxLon, maxLat });
344
+ this.globeShellWiggle?.setBBox({ minLon, minLat, maxLon, maxLat });
219
345
  this._drawTextureResolution = this._drawTextureSizeFromBbox({ minLon, minLat, maxLon, maxLat });
220
- this.waveUbo.updateSingle("draw_texture_size", new Float32Array([this._drawTextureResolution.width, this._drawTextureResolution.height]));
346
+ this.waveUbo?.updateSingle("draw_texture_size", new Float32Array([this._drawTextureResolution.width, this._drawTextureResolution.height]));
221
347
  this._drawTextures = [this._createDrawTexture(), this._createDrawTexture()];
222
348
  }
223
349
  __readAndWriteTextureData() {
224
- const { gl, _rgVectorFieldTexture, _dataWidth, _dataHeight } = this;
225
- // Step 1: Bind the texture to a framebuffer
226
- // Step 2: Read the texture data into a Float32Array
350
+ const { gl, _rgVectorFieldTexture } = this;
351
+ if (!gl || !_rgVectorFieldTexture)
352
+ return;
227
353
  gl.bindTexture(gl.TEXTURE_2D, _rgVectorFieldTexture);
228
- const textureData = new Float32Array(_dataWidth * _dataHeight * 2); // RG32F has 2 components per pixel
229
- gl.readPixels(0, 0, _dataWidth, _dataHeight, gl.RG, gl.FLOAT, textureData);
354
+ const textureData = new Float32Array(this.options.dataWidth * this.options.dataHeight * 2);
355
+ gl.readPixels(0, 0, this.options.dataWidth, this.options.dataHeight, gl.RG, gl.FLOAT, textureData);
230
356
  gl.bindTexture(gl.TEXTURE_2D, null);
231
- // Step 3: Create a new texture and write the data back
232
- // Step 4: Clean up
233
357
  gl.deleteTexture(this._rgVectorFieldTexture);
234
- // Replace the old texture with the new one
235
358
  this._rgVectorFieldTexture = this._createRGTexture();
236
359
  this.setVectorFieldData(textureData);
237
360
  }
@@ -246,12 +369,12 @@ export default class Plugin {
246
369
  const vertical = maxLat - minLat;
247
370
  let width, height;
248
371
  if (horizon > vertical) {
249
- width = this._drawTextureMaxPixelOnDimension;
250
- height = Math.floor(this._drawTextureMaxPixelOnDimension * vertical / horizon);
372
+ width = this.options.drawTextureMaxPixelOnDimension;
373
+ height = Math.floor(this.options.drawTextureMaxPixelOnDimension * vertical / horizon);
251
374
  }
252
375
  else {
253
- height = this._drawTextureMaxPixelOnDimension;
254
- width = Math.floor(this._drawTextureMaxPixelOnDimension * horizon / vertical);
376
+ height = this.options.drawTextureMaxPixelOnDimension;
377
+ width = Math.floor(this.options.drawTextureMaxPixelOnDimension * horizon / vertical);
255
378
  }
256
379
  return {
257
380
  width,
@@ -259,11 +382,12 @@ export default class Plugin {
259
382
  };
260
383
  }
261
384
  _createRGTexture() {
262
- const { gl, _dataWidth, _dataHeight, } = this;
385
+ const { gl } = this;
386
+ if (!gl)
387
+ return null;
263
388
  const texture = gl.createTexture();
264
- // R32F
265
389
  gl.bindTexture(gl.TEXTURE_2D, texture);
266
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RG32F, _dataWidth, _dataHeight, 0, gl.RG, gl.FLOAT, null);
390
+ gl.texImage2D(gl.TEXTURE_2D, 0, gl.RG32F, this.options.dataWidth, this.options.dataHeight, 0, gl.RG, gl.FLOAT, null);
267
391
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
268
392
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
269
393
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
@@ -273,9 +397,10 @@ export default class Plugin {
273
397
  }
274
398
  _createDrawTexture() {
275
399
  const gl = this.gl;
400
+ if (!gl)
401
+ return null;
276
402
  const texture = gl.createTexture();
277
403
  gl.bindTexture(gl.TEXTURE_2D, texture);
278
- // gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 2200, 2200, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
279
404
  gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, this._drawTextureResolution.width, this._drawTextureResolution.height, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
280
405
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
281
406
  gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
@@ -0,0 +1,61 @@
1
+ export function imageToFields(data, legendData) {
2
+ const { image, uMin, vMin, uMax, vMax } = data;
3
+ const width = image.width;
4
+ const height = image.height;
5
+ const canvas = document.createElement("canvas");
6
+ canvas.width = width;
7
+ canvas.height = height;
8
+ const ctx = canvas.getContext("2d");
9
+ ctx?.drawImage(image, 0, 0);
10
+ const imageData = ctx?.getImageData(0, 0, width, height);
11
+ if (!imageData) {
12
+ throw new Error("Failed to get image data from canvas.");
13
+ }
14
+ const vectorFieldData = new Float32Array(width * height * 2);
15
+ const speedFieldData = new Float32Array(width * height);
16
+ const colorFieldData = new Uint8Array(width * height * 4);
17
+ // Pre-parse color values for better performance
18
+ const parsedColors = legendData.values.map(hex => {
19
+ const colorValue = parseInt(hex.replace("#", ""), 16);
20
+ return {
21
+ r: (colorValue >> 16) & 0xff,
22
+ g: (colorValue >> 8) & 0xff,
23
+ b: colorValue & 0xff
24
+ };
25
+ });
26
+ const uDiff = uMax - uMin;
27
+ const vDiff = vMax - vMin;
28
+ const uHeight = Math.max(Math.abs(uMin), Math.abs(uMax));
29
+ const scaler = Math.max(Math.abs(vMin), Math.abs(vMax), uHeight);
30
+ for (let y = 0; y < height; y++) {
31
+ for (let x = 0; x < width; x++) {
32
+ const index = (y * width + x) * 4;
33
+ const r = imageData.data[index];
34
+ const g = imageData.data[index + 1];
35
+ // Map texel [0,255] to actual speed values [uMin,uMax] and [vMin,vMax]
36
+ const uSpeed = uMin + (r / 255) * uDiff;
37
+ const vSpeed = vMin + (g / 255) * vDiff;
38
+ // Normalize to [-1,1] range for vector field
39
+ const u = uMin + (uDiff * r) / 255;
40
+ const v = vMin + (vDiff * g) / 255;
41
+ vectorFieldData[(y * width + x) * 2] = u / scaler;
42
+ vectorFieldData[(y * width + x) * 2 + 1] = -v / scaler;
43
+ const speed = Math.sqrt(uSpeed * uSpeed + vSpeed * vSpeed);
44
+ speedFieldData[y * width + x] = speed;
45
+ // Determine color based on speed and legendData
46
+ let colorIndex = legendData.thresholds.length - 1;
47
+ for (let i = 0; i < legendData.thresholds.length; i++) {
48
+ if (speed < legendData.thresholds[i]) {
49
+ colorIndex = i;
50
+ break;
51
+ }
52
+ }
53
+ const color = parsedColors[colorIndex];
54
+ colorFieldData[index] = color.r;
55
+ colorFieldData[index + 1] = color.g;
56
+ colorFieldData[index + 2] = color.b;
57
+ colorFieldData[index + 3] = 255; // Alpha channel
58
+ }
59
+ }
60
+ return { vectorFieldData, speedFieldData, colorFieldData };
61
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -1,16 +1,10 @@
1
- /**
2
- * @param {ImageData} imageData
3
- * image
4
- * uMax
5
- * vMax
6
- * height
7
- * width
8
- * @returns {Float32Array}
9
- */
10
1
  export default function imageToMagnitude(imageData) {
11
2
  const { image, uMax, vMax, uMin, vMin, height, width } = imageData;
12
3
  const canvas = document.createElement('canvas');
13
4
  const ctx = canvas.getContext('2d');
5
+ if (!ctx) {
6
+ throw new Error('Failed to get 2D context');
7
+ }
14
8
  canvas.width = width;
15
9
  canvas.height = height;
16
10
  ctx.drawImage(image, 0, 0, width, height);
@@ -34,6 +28,9 @@ export function imageToRadianAngle(imageData) {
34
28
  const { image, uMax, vMax, uMin, vMin, height, width } = imageData;
35
29
  const canvas = document.createElement('canvas');
36
30
  const ctx = canvas.getContext('2d');
31
+ if (!ctx) {
32
+ throw new Error('Failed to get 2D context');
33
+ }
37
34
  canvas.width = width;
38
35
  canvas.height = height;
39
36
  ctx.drawImage(image, 0, 0, width, height);