@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.
- package/Math/haversine.js +22 -0
- package/Math/methods.js +15 -2
- package/Math/tessellation/methods.js +4 -1
- package/Math/tessellation/nearest-value-padding.js +112 -0
- package/Math/tessellation/spherical-triangle-area.js +99 -0
- package/Math/tessellation/tile-merger.js +346 -215
- package/Math/tessellation/triangle-tessellation.js +381 -9
- package/Math/vec3.js +4 -0
- package/Math/xyz-tile.js +18 -0
- package/altitude-locator/plugin.js +1 -2
- package/investigation-tools/draw/tiles/adapters.js +2 -2
- package/investigation-tools/draw/tiles/tiles.js +2 -2
- package/package.json +1 -1
- package/programs/helpers/fadeaway.js +6 -1
- package/programs/point-on-globe/square-pixel-point.js +1 -0
- package/programs/polygon-on-globe/texture-dem-triangles.js +94 -116
- package/programs/totems/camera-totem-attactment-interface.js +1 -0
- package/programs/totems/camerauniformblock.js +33 -22
- package/programs/totems/dem-textures-manager.js +265 -0
- package/programs/vectorfields/logics/drawrectangleparticles.js +51 -18
- package/programs/vectorfields/logics/{ubo-new.js → particle-ubo.js} +5 -14
- package/programs/vectorfields/logics/pixelbased.js +42 -36
- package/programs/vectorfields/pingpongbuffermanager.js +34 -8
- package/semiplugins/shape-on-terrain/terrain-polygon/adapters.js +55 -0
- package/semiplugins/shape-on-terrain/terrain-polygon/data/cache.js +102 -0
- package/semiplugins/shape-on-terrain/terrain-polygon/data/index-polygon-map.js +45 -0
- package/semiplugins/shape-on-terrain/terrain-polygon/data/manager.js +4 -0
- package/semiplugins/shape-on-terrain/terrain-polygon/data/master-worker.js +177 -0
- package/semiplugins/shape-on-terrain/terrain-polygon/data/polygon-to-triangles.js +100 -0
- package/semiplugins/shape-on-terrain/terrain-polygon/data/random.js +121 -0
- package/semiplugins/shape-on-terrain/terrain-polygon/data/types.js +1 -0
- package/semiplugins/shape-on-terrain/terrain-polygon/data/worker-contact.js +63 -0
- package/semiplugins/shape-on-terrain/terrain-polygon/data/worker.js +125 -0
- package/semiplugins/shape-on-terrain/terrain-polygon/terrain-polygon.js +219 -0
- package/semiplugins/shape-on-terrain/terrain-polygon/types.js +8 -0
- package/semiplugins/shell/bbox-renderer/logic.js +18 -58
- package/semiplugins/shell/bbox-renderer/object.js +19 -9
- package/tracks/point-heat-map/point-to-heat-map-flow.js +1 -1
- package/tracks/point-tracks/plugin.js +13 -6
- package/tracks/timetracks/program-line-strip.js +1 -1
- package/util/account/single-attribute-buffer-management/buffer-manager.js +5 -3
- package/util/account/single-attribute-buffer-management/buffer-orchestrator.js +2 -2
- package/util/gl-util/uniform-block/manager.js +20 -10
- package/util/helper-methods.js +8 -0
- package/util/picking/fence.js +4 -2
- package/util/picking/picker-displayer.js +51 -9
- package/util/programs/draw-texture-on-canvas.js +18 -15
- package/util/shaderfunctions/geometrytransformations.js +67 -1
- package/vectorfield/waveparticles/plugin.js +241 -116
- package/vectorfield/wind/adapters/image-to-fields.js +61 -0
- package/vectorfield/wind/adapters/types.js +1 -0
- package/vectorfield/wind/imagetovectorfieldandmagnitude.js +6 -9
- package/vectorfield/wind/plugin-persistant copy.js +364 -0
- package/vectorfield/wind/plugin-persistant.js +375 -0
- package/vectorfield/wind/plugin.js +1 -1
- package/Math/tessellation/earcut/adapters.js +0 -37
- package/Math/tessellation/hybrid-triangle-tessellation-meta.js +0 -123
- package/Math/tessellation/shred-input.js +0 -18
- package/Math/tessellation/tiler.js +0 -50
- package/Math/tessellation/triangle-tessellation-meta.js +0 -523
- package/programs/polygon-on-globe/texture-dem-triangle-test-plugin-triangle.js +0 -328
- package/programs/vectorfields/logics/drawrectangleparticles1.js +0 -112
- package/semiplugins/shape-on-terrain/terrain-cover/texture-dem-cover.js +0 -1
- package/util/gl-util/uniform-block/types.js +0 -1
- package/util/webglobe/index.js +0 -2
- /package/Math/tessellation/{zoom-catch.js → constants.js} +0 -0
- /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/
|
|
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 {
|
|
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
|
-
|
|
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.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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.
|
|
91
|
+
this.bufferManager = new PingPongBufferManager(gl, this.options.particleCount, inPositionLocation);
|
|
56
92
|
this._rgVectorFieldTexture = this._createRGTexture();
|
|
57
|
-
this.waveUbo =
|
|
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.
|
|
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
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
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
|
-
|
|
179
|
+
if (!gl)
|
|
180
|
+
return;
|
|
126
181
|
FadeAwayProgramCache.release(gl);
|
|
127
|
-
globeShellWiggle
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
pixelBasedMoveProgramCache.
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
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
|
-
|
|
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.
|
|
143
|
-
this.bufferManager
|
|
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.
|
|
148
|
-
this.
|
|
207
|
+
this.options.dataWidth = dataWidth;
|
|
208
|
+
this.options.dataHeight = dataHeight;
|
|
149
209
|
}
|
|
150
210
|
if (flipY !== undefined) {
|
|
151
|
-
this.
|
|
211
|
+
this.options.flipY = flipY;
|
|
152
212
|
}
|
|
153
213
|
this.__data = data;
|
|
154
|
-
const { gl
|
|
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.
|
|
157
|
-
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RG32F,
|
|
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
|
|
225
|
+
this.globeShellWiggle?.setHeight(value);
|
|
163
226
|
}
|
|
164
227
|
setDropRate(value) {
|
|
165
|
-
this.waveUbo
|
|
166
|
-
;
|
|
228
|
+
this.waveUbo?.updateSingle("drop_rate", new Float32Array([value]));
|
|
167
229
|
}
|
|
168
230
|
setDrawTextureMaxPixelOnDimension(value) {
|
|
169
|
-
this.
|
|
231
|
+
this.options.drawTextureMaxPixelOnDimension = value;
|
|
170
232
|
this._drawTextureResolution = this._drawTextureSizeFromBbox(this._globeshellparameters);
|
|
171
|
-
this.waveUbo
|
|
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
|
|
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.
|
|
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
|
|
258
|
+
this.globeShellWiggle?.setOpacity(value);
|
|
197
259
|
}
|
|
198
260
|
setParticleDimensions(tail, wing) {
|
|
199
261
|
if (0 < tail && 0 < wing) {
|
|
200
|
-
this.
|
|
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
|
|
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
|
|
344
|
+
this.globeShellWiggle?.setBBox({ minLon, minLat, maxLon, maxLat });
|
|
219
345
|
this._drawTextureResolution = this._drawTextureSizeFromBbox({ minLon, minLat, maxLon, maxLat });
|
|
220
|
-
this.waveUbo
|
|
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
|
|
225
|
-
|
|
226
|
-
|
|
350
|
+
const { gl, _rgVectorFieldTexture } = this;
|
|
351
|
+
if (!gl || !_rgVectorFieldTexture)
|
|
352
|
+
return;
|
|
227
353
|
gl.bindTexture(gl.TEXTURE_2D, _rgVectorFieldTexture);
|
|
228
|
-
const textureData = new Float32Array(
|
|
229
|
-
gl.readPixels(0, 0,
|
|
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.
|
|
250
|
-
height = Math.floor(this.
|
|
372
|
+
width = this.options.drawTextureMaxPixelOnDimension;
|
|
373
|
+
height = Math.floor(this.options.drawTextureMaxPixelOnDimension * vertical / horizon);
|
|
251
374
|
}
|
|
252
375
|
else {
|
|
253
|
-
height = this.
|
|
254
|
-
width = Math.floor(this.
|
|
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
|
|
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,
|
|
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);
|