@pirireis/webglobeplugins 0.12.0-alpha → 0.14.0-alpha
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/arc-cdf-points.js +20 -0
- package/Math/arc-generate-points copy.js +1 -0
- package/Math/arc.js +21 -8
- package/Math/circle-cdf-points.js +0 -2
- package/Math/circle.js +35 -16
- package/Math/templete-shapes/grid-visually-equal.js +66 -0
- package/altitude-locator/plugin.js +3 -2
- package/bearing-line/plugin.js +1 -2
- package/circle-line-chain/plugin.js +4 -7
- package/compass-rose/compass-rose-padding-flat.js +12 -0
- package/package.json +1 -1
- package/programs/line-on-globe/degree-padding-around-circle-3d.js +1 -1
- package/programs/line-on-globe/linestrip/data.js +4 -0
- package/programs/line-on-globe/{linestrip.js → linestrip/linestrip.js} +37 -35
- package/programs/line-on-globe/naive-accurate-flexible.js +23 -16
- package/programs/picking/pickable-renderer.js +1 -2
- package/programs/rings/distancering/circleflatprogram.js +116 -120
- package/programs/rings/distancering/circlepaddingfreeangleprogram.js +1 -1
- package/programs/rings/distancering/circlepaddysharedbuffer.js +368 -354
- package/programs/rings/distancering/index.js +6 -5
- package/programs/rings/distancering/paddyflatprogram.js +127 -136
- package/programs/rings/distancering/paddyflatprogram2d.js +129 -138
- package/programs/rings/distancering/paddyflatprogram3d.js +128 -136
- package/programs/rings/partial-ring/piece-of-pie copy.js +286 -0
- package/programs/rings/partial-ring/piece-of-pie.js +26 -13
- package/programs/totems/camerauniformblock.js +1 -1
- package/programs/totems/index.js +1 -1
- package/programs/vectorfields/logics/pixelbased.js +5 -21
- package/range-tools-on-terrain/bearing-line/adapters.js +111 -0
- package/range-tools-on-terrain/bearing-line/plugin.js +360 -0
- package/range-tools-on-terrain/bearing-line/types.js +1 -0
- package/range-tools-on-terrain/circle-line-chain/adapters.js +83 -0
- package/range-tools-on-terrain/circle-line-chain/chain-list-map.js +351 -0
- package/range-tools-on-terrain/circle-line-chain/plugin.js +389 -0
- package/range-tools-on-terrain/circle-line-chain/types.js +1 -0
- package/range-tools-on-terrain/range-ring/adapters.js +25 -0
- package/range-tools-on-terrain/range-ring/plugin.js +31 -0
- package/range-tools-on-terrain/range-ring/types.js +1 -0
- package/rangerings/plugin.js +7 -11
- package/semiplugins/lightweight/line-plugin.js +195 -0
- package/semiplugins/lightweight/piece-of-pie-plugin.js +175 -0
- package/semiplugins/shape-on-terrain/arc-plugin.js +368 -0
- package/{shape-on-terrain/circle/plugin.js → semiplugins/shape-on-terrain/circle-plugin.js} +129 -68
- package/semiplugins/shape-on-terrain/derived/padding-plugin.js +96 -0
- package/semiplugins/type.js +1 -0
- package/types.js +0 -11
- package/util/account/create-buffermap-orchastration.js +39 -0
- package/util/account/index.js +2 -2
- package/util/account/single-attribute-buffer-management/buffer-manager.js +2 -3
- package/util/account/single-attribute-buffer-management/buffer-orchestrator.js +14 -3
- package/util/build-strategy/static-dynamic.js +1 -1
- package/util/check/typecheck.js +15 -1
- package/util/geometry/index.js +3 -2
- package/util/gl-util/buffer/attribute-loader.js +2 -5
- package/util/gl-util/draw-options/methods.js +4 -5
- package/util/webglobjectbuilders.js +4 -9
- package/write-text/context-text3.js +17 -0
- package/write-text/context-text3old.js +152 -0
- package/programs/line-on-globe/circle-accurate.js +0 -175
- package/programs/line-on-globe/circle.js +0 -164
- package/programs/line-on-globe/to-the-surface.js +0 -109
- package/programs/rings/distancering/shader.js +0 -1
- package/programs/totems/canvas-webglobe-info1.js +0 -106
- package/shape-on-terrain/arc/naive/plugin.js +0 -205
- package/util/check/get.js +0 -14
- package/util/gl-util/buffer/types.js +0 -1
- package/util/gl-util/draw-options/types.js +0 -15
- package/util/webglobjectbuilders1.js +0 -219
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
// import { ArcOnTerrainPluginOptions } from "./type";
|
|
2
|
+
import { LineStripProgramCache } from "../../programs/line-on-globe/linestrip/linestrip";
|
|
3
|
+
import { createBufferAndReadInfo } from "../../util/gl-util/buffer/attribute-loader";
|
|
4
|
+
// import { populateFloat32Array } from "../../util/jshelpers/data-filler";
|
|
5
|
+
import { BufferManager, BufferOrchestrator } from "../../util/account/single-attribute-buffer-management/index";
|
|
6
|
+
import { globe3Dcoordinates, globe2Dcoordinates, RADIANS } from "../../Math/methods";
|
|
7
|
+
import { generateArcPoints, evenlySpacedArcPoints } from "../../Math/arc-cdf-points";
|
|
8
|
+
import { StaticDynamicState, StaticDynamicStrategy } from "../../util/build-strategy/static-dynamic";
|
|
9
|
+
import { opacityCheck } from "../../util/check/typecheck";
|
|
10
|
+
import * as vec3 from "../../Math/vec3";
|
|
11
|
+
import * as arc from "../../Math/arc";
|
|
12
|
+
import { CameraUniformBlockTotemCache } from "../../programs/totems/camerauniformblock";
|
|
13
|
+
import { WORLD_RADIUS_3D } from "../../Math/constants";
|
|
14
|
+
const DYNAMIC_STRATEGY_START_LOD = 12;
|
|
15
|
+
const INITAL_CAPACITY = 10;
|
|
16
|
+
const _attractionPoint = [0, 0, 0];
|
|
17
|
+
const _start = [0, 0, 0];
|
|
18
|
+
const _end = [0, 0, 0];
|
|
19
|
+
const _0arc = arc.create([1, 0, 0], [0, 1, 0]); // zero arc for intersection tests
|
|
20
|
+
export class ArcOnTerrainPlugin {
|
|
21
|
+
id;
|
|
22
|
+
program = null;
|
|
23
|
+
bufferManagerMap = null;
|
|
24
|
+
_bufferOrchestrator = new BufferOrchestrator({ capacity: INITAL_CAPACITY });
|
|
25
|
+
_opacity = 1;
|
|
26
|
+
_arcUBOHandler = null;
|
|
27
|
+
_vao = null;
|
|
28
|
+
globe = null;
|
|
29
|
+
gl = null;
|
|
30
|
+
_arcMap;
|
|
31
|
+
_cameraUniformBlock = null;
|
|
32
|
+
_staticDynamicStrategy = null;
|
|
33
|
+
_options;
|
|
34
|
+
_coordinaateBufferKeysForUpdate = [];
|
|
35
|
+
_freed = false;
|
|
36
|
+
constructor(id, { globeViewOn = true, // If true, arcs are drawn in 3D globe view
|
|
37
|
+
flatViewOn = true, // If true, arcs are drawn in 2D flat view
|
|
38
|
+
variativeColorsOn = false, defaultColor = [0.1, 0.1, 1, 1], // Default color in RGBA format
|
|
39
|
+
defaultHeightFromGroundIn3D = 30.0, // Default height from ground in
|
|
40
|
+
vertexCount = 70, // Number of vertices per arc
|
|
41
|
+
cameraAttractionIsOn = true // If true, camera attraction is enabled else evenly distributed arc points are used
|
|
42
|
+
} = {}) {
|
|
43
|
+
this.id = id;
|
|
44
|
+
this._arcMap = new Map();
|
|
45
|
+
this._options = {
|
|
46
|
+
globeViewOn: globeViewOn ? true : false,
|
|
47
|
+
flatViewOn: flatViewOn ? true : false,
|
|
48
|
+
variativeColorsOn: variativeColorsOn ? true : false,
|
|
49
|
+
defaultColor: defaultColor ? defaultColor : [0.1, 0.1, 1, 1],
|
|
50
|
+
defaultHeightFromGroundIn3D: defaultHeightFromGroundIn3D ? defaultHeightFromGroundIn3D : 30.0,
|
|
51
|
+
vertexCount: vertexCount ? vertexCount : 70,
|
|
52
|
+
cameraAttractionIsOn: cameraAttractionIsOn ? cameraAttractionIsOn : true
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
insertBulk(arcs) {
|
|
56
|
+
const keys = [];
|
|
57
|
+
for (let arcInput of arcs) {
|
|
58
|
+
const { key, start, end } = arcInput;
|
|
59
|
+
if (this._arcMap.has(key)) {
|
|
60
|
+
this._arcMap.delete(key);
|
|
61
|
+
}
|
|
62
|
+
vec3.fromLongLatToUnitVector(_start, [start[0] * RADIANS, start[1] * RADIANS]);
|
|
63
|
+
vec3.fromLongLatToUnitVector(_end, [end[0] * RADIANS, end[1] * RADIANS]);
|
|
64
|
+
const _arc = arc.create(_start, _end);
|
|
65
|
+
this._arcMap.set(key, [_arc, arcInput]); // height is null for now
|
|
66
|
+
keys.push(key);
|
|
67
|
+
}
|
|
68
|
+
this.__buildStaticArcs(keys, true);
|
|
69
|
+
this.globe.DrawRender();
|
|
70
|
+
}
|
|
71
|
+
deleteBulk(keys) {
|
|
72
|
+
if (!this._bufferOrchestrator || !this.bufferManagerMap || !this.globe) {
|
|
73
|
+
console.warn("Buffer orchestrator or buffer manager map is not initialized.");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
for (const key of keys) {
|
|
77
|
+
if (this._arcMap.has(key)) {
|
|
78
|
+
this._arcMap.delete(key);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
console.warn(`Arc with key ${key} not found in arcMap.`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
this._bufferOrchestrator.deleteBulk(keys, this.bufferManagerMap);
|
|
85
|
+
this.globe.DrawRender();
|
|
86
|
+
}
|
|
87
|
+
updateColor(key, color) {
|
|
88
|
+
if (this._options.variativeColorsOn === false) {
|
|
89
|
+
console.warn("Variative colors are not enabled in this plugin. Create another instance with variativeColorsOn: true to use this feature.");
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const { _bufferOrchestrator, bufferManagerMap } = this;
|
|
93
|
+
if (!_bufferOrchestrator || !bufferManagerMap) {
|
|
94
|
+
console.warn("Buffer orchestrator or buffer manager map is not initialized.");
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
const arcInput = this._arcMap.get(key);
|
|
98
|
+
if (!arcInput) {
|
|
99
|
+
console.warn(`Arc with key ${key} not found in arcMap.`);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
arcInput[1].color = color; // Update the color in the arc map
|
|
103
|
+
_bufferOrchestrator.updateBulk([{ key: key, color: color }], bufferManagerMap, ["color"]);
|
|
104
|
+
this.globe.DrawRender();
|
|
105
|
+
}
|
|
106
|
+
setPluginOpacity(opacity) {
|
|
107
|
+
if (!this.globe || !this.gl) {
|
|
108
|
+
console.warn("Globe or WebGL context is not initialized.");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
opacityCheck(opacity);
|
|
112
|
+
this._opacity = opacity;
|
|
113
|
+
this.globe.DrawRender();
|
|
114
|
+
}
|
|
115
|
+
setCameraAttraction(isOn) {
|
|
116
|
+
if (!this.globe || !this.gl) {
|
|
117
|
+
console.warn("Globe or WebGL context is not initialized.");
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
this._options.cameraAttractionIsOn = isOn;
|
|
121
|
+
this.globe.DrawRender();
|
|
122
|
+
}
|
|
123
|
+
init(globe, gl) {
|
|
124
|
+
this.globe = globe;
|
|
125
|
+
this.gl = gl;
|
|
126
|
+
this.program = LineStripProgramCache.get(globe);
|
|
127
|
+
this._staticDynamicStrategy = new StaticDynamicStrategy(globe, DYNAMIC_STRATEGY_START_LOD); // Initialize static-dynamic strategy with a transition level of 8
|
|
128
|
+
this._cameraUniformBlock = CameraUniformBlockTotemCache.get(globe);
|
|
129
|
+
this._fillBufferManagerMap();
|
|
130
|
+
this._bufferOrchestrator = new BufferOrchestrator();
|
|
131
|
+
this._arcUBOHandler = this.program.createUBO();
|
|
132
|
+
this._arcUBOHandler.update(new Map([
|
|
133
|
+
["u_color", new Float32Array(this._options.defaultColor)],
|
|
134
|
+
]));
|
|
135
|
+
}
|
|
136
|
+
draw3D() {
|
|
137
|
+
// Drawing logic here
|
|
138
|
+
if (!this.globe || !this.gl) {
|
|
139
|
+
console.warn("Globe or WebGL context is not initialized.");
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
this._staticDynamicStrategy?.updateState();
|
|
143
|
+
this._buildArcs(); // can be async
|
|
144
|
+
const { gl, program, _bufferOrchestrator, _vao, _arcUBOHandler, } = this;
|
|
145
|
+
if (!gl || !program || !_bufferOrchestrator || !_vao || !_arcUBOHandler) {
|
|
146
|
+
console.warn("WebGL context, program, or buffer orchestrator is not initialized.");
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
gl.disable(gl.DEPTH_TEST);
|
|
150
|
+
const drawOptions = {
|
|
151
|
+
drawRange: {
|
|
152
|
+
first: 0,
|
|
153
|
+
count: _bufferOrchestrator.length * (this._options.vertexCount + 1)
|
|
154
|
+
},
|
|
155
|
+
indexes: null
|
|
156
|
+
};
|
|
157
|
+
program.draw(_vao, drawOptions, this._opacity, _arcUBOHandler);
|
|
158
|
+
gl.enable(gl.DEPTH_TEST);
|
|
159
|
+
}
|
|
160
|
+
_buildArcs() {
|
|
161
|
+
const state = this._staticDynamicStrategy?.getState();
|
|
162
|
+
if (state === StaticDynamicState.TO_STATIC) {
|
|
163
|
+
this.__buildStaticArcs();
|
|
164
|
+
}
|
|
165
|
+
else if (state === StaticDynamicState.DYNAMIC) {
|
|
166
|
+
this.__buildArcs();
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
__buildStaticArcs(keys = [], calledFromInsert = false) {
|
|
170
|
+
const { globe, _arcMap, _cameraUniformBlock, bufferManagerMap, _bufferOrchestrator } = this;
|
|
171
|
+
if (!globe || !_cameraUniformBlock || !bufferManagerMap || !_bufferOrchestrator) {
|
|
172
|
+
console.warn("Globe or camera uniform block is not initialized.");
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
const longLat = [0, 0];
|
|
176
|
+
const _attractionStrength = 0;
|
|
177
|
+
const longLatArr = new Float32Array(2 * this._options.vertexCount);
|
|
178
|
+
const data = [{
|
|
179
|
+
key: "staticArcs",
|
|
180
|
+
longLatArr: longLatArr,
|
|
181
|
+
height: null,
|
|
182
|
+
color: this._options.defaultColor
|
|
183
|
+
}];
|
|
184
|
+
if (keys.length == 0) {
|
|
185
|
+
for (const [key, [arcInstance, arcInput]] of _arcMap) {
|
|
186
|
+
arc.copy(_0arc, arcInstance);
|
|
187
|
+
const generatedPoints = evenlySpacedArcPoints(_0arc.p0, _0arc.normal, _0arc.coverAngle, this._options.vertexCount);
|
|
188
|
+
for (let i = 0; i < generatedPoints.length; i++) {
|
|
189
|
+
const point = generatedPoints[i];
|
|
190
|
+
vec3.fromUnitVectorToLongLat(longLat, point);
|
|
191
|
+
longLatArr.set([longLat[0] / RADIANS, longLat[1] / RADIANS], i * 2);
|
|
192
|
+
}
|
|
193
|
+
data[0].key = key;
|
|
194
|
+
data[0].height = arcInput.height ?? this._options.defaultHeightFromGroundIn3D;
|
|
195
|
+
data[0].color = arcInput.color ?? this._options.defaultColor;
|
|
196
|
+
if (calledFromInsert) {
|
|
197
|
+
this._bufferOrchestrator.insertBulk(data, bufferManagerMap);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
this._bufferOrchestrator.updateBulk(data, bufferManagerMap, this._coordinaateBufferKeysForUpdate);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
for (let key of keys) {
|
|
206
|
+
if (!_arcMap.has(key)) {
|
|
207
|
+
console.warn(`Arc with key ${key} not found in arcMap.`);
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
const [arcInstance, arcInput] = _arcMap.get(key);
|
|
211
|
+
arc.copy(_0arc, arcInstance);
|
|
212
|
+
const generatedPoints = evenlySpacedArcPoints(_0arc.p0, _0arc.normal, _0arc.coverAngle, this._options.vertexCount);
|
|
213
|
+
for (let i = 0; i < generatedPoints.length; i++) {
|
|
214
|
+
const point = generatedPoints[i];
|
|
215
|
+
vec3.fromUnitVectorToLongLat(longLat, point);
|
|
216
|
+
longLatArr.set([longLat[0] / RADIANS, longLat[1] / RADIANS], i * 2);
|
|
217
|
+
}
|
|
218
|
+
data[0].key = key;
|
|
219
|
+
data[0].height = arcInput.height ?? this._options.defaultHeightFromGroundIn3D;
|
|
220
|
+
data[0].color = arcInput.color ?? this._options.defaultColor;
|
|
221
|
+
if (calledFromInsert) {
|
|
222
|
+
this._bufferOrchestrator.insertBulk(data, bufferManagerMap);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
this._bufferOrchestrator.updateBulk(data, bufferManagerMap, this._coordinaateBufferKeysForUpdate);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
__buildArcs() {
|
|
231
|
+
const { globe, _arcMap, _cameraUniformBlock, bufferManagerMap } = this;
|
|
232
|
+
if (!globe || !_cameraUniformBlock || !bufferManagerMap) {
|
|
233
|
+
console.warn("Globe or camera uniform block is not initialized.");
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
const { cameraAttractionIsOn } = this._options;
|
|
237
|
+
const lookAtPosition = _cameraUniformBlock.getLookAtVector();
|
|
238
|
+
const cameraPosition = _cameraUniformBlock.getCameraVector();
|
|
239
|
+
vec3.divideScalar(cameraPosition, cameraPosition, WORLD_RADIUS_3D);
|
|
240
|
+
vec3.add(cameraPosition, cameraPosition, lookAtPosition);
|
|
241
|
+
// vec3.normalize(cameraPosition, cameraPosition);
|
|
242
|
+
vec3.divideScalar(cameraPosition, cameraPosition, 2);
|
|
243
|
+
// vec3.multiplyScalar(cameraPosition, cameraPosition, 1.3);
|
|
244
|
+
const _attractionStrength = (() => {
|
|
245
|
+
const lod = globe.api_GetCurrentLODWithDecimal();
|
|
246
|
+
if (lod < 11) {
|
|
247
|
+
return 1;
|
|
248
|
+
}
|
|
249
|
+
return lod * (lod - 8.5);
|
|
250
|
+
})();
|
|
251
|
+
const result = [];
|
|
252
|
+
const longLat = [0, 0];
|
|
253
|
+
for (const [key, [arcInstance, arcInput]] of _arcMap) {
|
|
254
|
+
arc.copy(_0arc, arcInstance);
|
|
255
|
+
const isOnArc = arc.closestPoint(_attractionPoint, _0arc, cameraPosition);
|
|
256
|
+
if (!isOnArc) {
|
|
257
|
+
if (vec3.distanceSquared(cameraPosition, _0arc.p0) < vec3.distanceSquared(cameraPosition, _0arc.p1)) {
|
|
258
|
+
vec3.copy(_attractionPoint, _0arc.p0);
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
vec3.copy(_attractionPoint, _0arc.p1);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
const generatedPoints = cameraAttractionIsOn ?
|
|
265
|
+
generateArcPoints(_0arc.p0, _0arc.p1, _0arc.normal, _attractionPoint, this._options.vertexCount, _attractionStrength)
|
|
266
|
+
: evenlySpacedArcPoints(_0arc.p0, _0arc.normal, _0arc.coverAngle, this._options.vertexCount);
|
|
267
|
+
const longLatArr = new Float32Array(2 * this._options.vertexCount);
|
|
268
|
+
for (let i = 0; i < generatedPoints.length; i++) {
|
|
269
|
+
const point = generatedPoints[i];
|
|
270
|
+
vec3.fromUnitVectorToLongLat(longLat, point);
|
|
271
|
+
longLatArr.set([longLat[0] / RADIANS, longLat[1] / RADIANS], i * 2);
|
|
272
|
+
}
|
|
273
|
+
result.push({
|
|
274
|
+
key: key,
|
|
275
|
+
longLatArr: longLatArr,
|
|
276
|
+
height: arcInput.height ?? this._options.defaultHeightFromGroundIn3D,
|
|
277
|
+
// color: arcInput.color // Use the color from the arc map
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
// this._bufferOrchestrator.resetWithCapacity(bufferManagerMap, result.length);
|
|
281
|
+
this._bufferOrchestrator.insertBulk(result, bufferManagerMap, ["position3d", "position2d"]);
|
|
282
|
+
}
|
|
283
|
+
_fillBufferManagerMap() {
|
|
284
|
+
const { gl, globe } = this;
|
|
285
|
+
if (!gl || !globe) {
|
|
286
|
+
console.warn("WebGL context or globe is not initialized.");
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
const g2D = globe2Dcoordinates(globe);
|
|
290
|
+
const { flatViewOn, globeViewOn, variativeColorsOn } = this._options;
|
|
291
|
+
this.bufferManagerMap = new Map();
|
|
292
|
+
if (globeViewOn) {
|
|
293
|
+
this.bufferManagerMap.set("position3d", {
|
|
294
|
+
bufferManager: new BufferManager(gl, 3 * (this._options.vertexCount + 1), { bufferType: "STREAM_DRAW", initialCapacity: INITAL_CAPACITY }),
|
|
295
|
+
adaptor: (item) => {
|
|
296
|
+
const { longLatArr, height = this._options.defaultHeightFromGroundIn3D } = item;
|
|
297
|
+
const result = globe3Dcoordinates(globe, height)(longLatArr, { paddingCount: 1 });
|
|
298
|
+
return result;
|
|
299
|
+
}
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
if (flatViewOn) {
|
|
303
|
+
this.bufferManagerMap.set("position2d", {
|
|
304
|
+
bufferManager: new BufferManager(gl, 2 * (this._options.vertexCount + 1), { bufferType: "STREAM_DRAW", initialCapacity: INITAL_CAPACITY }),
|
|
305
|
+
adaptor: (item) => {
|
|
306
|
+
const { longLatArr } = item;
|
|
307
|
+
return g2D(longLatArr, { paddingCount: 1 });
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
if (this._options.variativeColorsOn) {
|
|
312
|
+
const _colorArray = new Float32Array(4 * (this._options.vertexCount + 1));
|
|
313
|
+
this.bufferManagerMap.set("color", {
|
|
314
|
+
bufferManager: new BufferManager(gl, 4 * (this._options.vertexCount + 1), { bufferType: "STREAM_DRAW", initialCapacity: INITAL_CAPACITY }),
|
|
315
|
+
adaptor: (item) => {
|
|
316
|
+
// Calculate color based on radius
|
|
317
|
+
if (item.color) {
|
|
318
|
+
for (let i = 0; i < this._options.vertexCount; i++) {
|
|
319
|
+
_colorArray.set(item.color, 4 * i); // Fill color for each vertex
|
|
320
|
+
}
|
|
321
|
+
return _colorArray;
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
return _colorArray.fill(-1, 0, 4 * (this._options.vertexCount)); // Fill with -1 if no color is provided
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
const bufferObjects = [
|
|
330
|
+
globeViewOn ? "position3d" : null,
|
|
331
|
+
flatViewOn ? "position2d" : null,
|
|
332
|
+
variativeColorsOn ? "color" : null
|
|
333
|
+
].map((key) => {
|
|
334
|
+
if (!key)
|
|
335
|
+
return null;
|
|
336
|
+
this._coordinaateBufferKeysForUpdate.push(key);
|
|
337
|
+
// @ts-ignore
|
|
338
|
+
const bufferManager = this.bufferManagerMap.get(key).bufferManager;
|
|
339
|
+
// @ts-ignore
|
|
340
|
+
return createBufferAndReadInfo(bufferManager.buffer);
|
|
341
|
+
});
|
|
342
|
+
// @ts-ignore
|
|
343
|
+
this._vao = this.program.createVAO(bufferObjects[0], // position3d
|
|
344
|
+
bufferObjects[1], // position2d
|
|
345
|
+
bufferObjects[2] // color
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
free() {
|
|
349
|
+
if (this._freed) {
|
|
350
|
+
console.warn("Plugin already freed");
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
if (!this.gl || !this.program) {
|
|
354
|
+
console.warn("WebGL context or program is not initialized.");
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
this._arcUBOHandler?.free();
|
|
358
|
+
this.bufferManagerMap?.forEach((manager) => {
|
|
359
|
+
manager.bufferManager.free();
|
|
360
|
+
});
|
|
361
|
+
CameraUniformBlockTotemCache.release(this.globe);
|
|
362
|
+
LineStripProgramCache.release(this.globe);
|
|
363
|
+
this.gl = null;
|
|
364
|
+
this.globe = null;
|
|
365
|
+
this._arcMap.clear();
|
|
366
|
+
this._coordinaateBufferKeysForUpdate = [];
|
|
367
|
+
}
|
|
368
|
+
}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
import { LineStripProgramCache } from "../../programs/line-on-globe/linestrip";
|
|
1
|
+
import { LineStripProgramCache } from "../../programs/line-on-globe/linestrip/linestrip";
|
|
2
2
|
import { BufferManager, BufferOrchestrator } from "../../util/account/single-attribute-buffer-management/index";
|
|
3
3
|
import { CameraUniformBlockTotemCache } from "../../programs/totems/camerauniformblock";
|
|
4
|
-
import { createBufferAndReadInfo } from
|
|
4
|
+
import { createBufferAndReadInfo } from "../../util/gl-util/buffer/attribute-loader";
|
|
5
5
|
import * as CircleMethods from "../../Math/circle";
|
|
6
6
|
import * as CircleCDF from "../../Math/circle-cdf-points";
|
|
7
7
|
import * as vec3 from "../../Math/vec3";
|
|
8
8
|
import { globe3Dcoordinates, globe2Dcoordinates } from "../../Math/methods";
|
|
9
9
|
import { StaticDynamicStrategy, StaticDynamicState } from "../../util/build-strategy/static-dynamic";
|
|
10
10
|
import { WORLD_RADIUS_3D, WORLD_RADIUS_MERCATOR } from "../../Math/constants";
|
|
11
|
+
import { opacityCheck } from "../../util/check/typecheck";
|
|
12
|
+
const ACTIVATE_DYNAMIC_STRATEGY_AT_LEVEL = 8;
|
|
11
13
|
const CIRCLE_POINTS_COUNT_HALF = 65; // Half of the points plus one for the reflection
|
|
12
14
|
const CIRCLE_POINTS_COUNT = CIRCLE_POINTS_COUNT_HALF * 2 - 1; // Number of points to approximate the circle
|
|
13
15
|
const DENSE_PART_SAMPLE_RATIO = 0.80; // Ratio of the dense part of the circle
|
|
@@ -50,6 +52,7 @@ const defineStregthLevel = (lod, radiusMeters) => {
|
|
|
50
52
|
return Math.min(level, ATTRACTION_LEVELS - 1);
|
|
51
53
|
};
|
|
52
54
|
const AnglesStash = CircleCDF.createCummulativeTemplateStash(ATTRACTION_LEVELS, CIRCLE_POINTS_COUNT_HALF, DENSE_PART_SAMPLE_RATIO, STRENGTH_FACTOR);
|
|
55
|
+
const _colorArray = new Float32Array(4 * CIRCLE_POINTS_COUNT); // Used to store the color for each circle
|
|
53
56
|
export class CircleOnTerrainPlugin {
|
|
54
57
|
id;
|
|
55
58
|
globe = null;
|
|
@@ -65,18 +68,120 @@ export class CircleOnTerrainPlugin {
|
|
|
65
68
|
_vao = null;
|
|
66
69
|
_dobuild = true; // This is used to trigger the build of circles when the camera position changes.
|
|
67
70
|
_staticDynamicStrategy = null;
|
|
68
|
-
|
|
71
|
+
_styleOptions = {
|
|
72
|
+
variativeColorsOn: false,
|
|
73
|
+
defaultColor: [0.1, 0.1, 0.1, 1], // Default color in RGBA format
|
|
74
|
+
defaultHeightFromGroundIn3D: 30.0
|
|
75
|
+
};
|
|
76
|
+
constructor(id, styleOptions = {}) {
|
|
69
77
|
this.id = id;
|
|
78
|
+
this._styleOptions = { ...this._styleOptions, ...styleOptions };
|
|
79
|
+
this._styleOptions.defaultColor = new Float32Array(this._styleOptions.defaultColor);
|
|
70
80
|
}
|
|
81
|
+
init(globe, gl) {
|
|
82
|
+
this.globe = globe;
|
|
83
|
+
this.gl = gl;
|
|
84
|
+
// Initialize the program cache for line strip rendering.
|
|
85
|
+
this._staticDynamicStrategy = new StaticDynamicStrategy(globe, ACTIVATE_DYNAMIC_STRATEGY_AT_LEVEL);
|
|
86
|
+
this.lineProgram = LineStripProgramCache.get(globe);
|
|
87
|
+
// const g3D = globe3Dcoordinates(globe, 30);
|
|
88
|
+
const g2D = globe2Dcoordinates(globe);
|
|
89
|
+
// Initialize with a reasonable initial capacity to prevent buffer size issues
|
|
90
|
+
const initialCapacity = 100; // Start with capacity for 100 circles
|
|
91
|
+
this.bufferManagerMap.set("position3d", {
|
|
92
|
+
bufferManager: new BufferManager(gl, (CIRCLE_POINTS_COUNT + 1) * 3, { initialCapacity }), // plus 1 is for butting linestrips
|
|
93
|
+
adaptor: (item) => {
|
|
94
|
+
const { longLatArr, height = this._styleOptions.defaultHeightFromGroundIn3D } = item;
|
|
95
|
+
const result = globe3Dcoordinates(globe, height)(longLatArr, { paddingCount: 1, paddingValue: NaN });
|
|
96
|
+
return result;
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
this.bufferManagerMap.set("position2d", {
|
|
100
|
+
bufferManager: new BufferManager(gl, (CIRCLE_POINTS_COUNT + 1) * 2, { initialCapacity }),
|
|
101
|
+
adaptor: (item) => {
|
|
102
|
+
const { longLatArr } = item;
|
|
103
|
+
const result = g2D(longLatArr, { paddingCount: 1, paddingValue: NaN });
|
|
104
|
+
return result;
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
if (this._styleOptions.variativeColorsOn) {
|
|
108
|
+
this.bufferManagerMap.set("color", {
|
|
109
|
+
bufferManager: new BufferManager(gl, (CIRCLE_POINTS_COUNT + 1) * 4, { initialCapacity }),
|
|
110
|
+
adaptor: (item) => {
|
|
111
|
+
if (item.color) {
|
|
112
|
+
for (let i = 0; i < CIRCLE_POINTS_COUNT; i++) {
|
|
113
|
+
_colorArray.set(item.color, i * 4);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
for (let i = 0; i < CIRCLE_POINTS_COUNT; i++) {
|
|
118
|
+
_colorArray.set(this._styleOptions.defaultColor, i * 4);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return _colorArray;
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
this._vao = this.lineProgram.createVAO(createBufferAndReadInfo(this.bufferManagerMap.get("position3d")?.bufferManager.buffer), createBufferAndReadInfo(this.bufferManagerMap.get("position2d")?.bufferManager.buffer), this._styleOptions.variativeColorsOn ?
|
|
126
|
+
createBufferAndReadInfo(this.bufferManagerMap.get("color")?.bufferManager.buffer) : null);
|
|
127
|
+
this._circleUBOHandler = this.lineProgram.createUBO();
|
|
128
|
+
this._cameraUniformBlock = CameraUniformBlockTotemCache.get(globe);
|
|
129
|
+
}
|
|
130
|
+
insertBulk(circles) {
|
|
131
|
+
if (this._isFree)
|
|
132
|
+
return;
|
|
133
|
+
if (!this.globe || !this.gl || !this.lineProgram || !this.bufferOrchestrator ||
|
|
134
|
+
!this.bufferManagerMap || !this._vao || !this._circleUBOHandler) {
|
|
135
|
+
throw new Error("Plugin not initialized properly");
|
|
136
|
+
}
|
|
137
|
+
//
|
|
138
|
+
const fillStaticKeys = [];
|
|
139
|
+
for (const circleInput of circles) {
|
|
140
|
+
if (!circleInput.key || !circleInput.center || !circleInput.radius) {
|
|
141
|
+
console.warn(`CircleOnTerrainPlugin: Circle input is missing required properties: ${circleInput.radius} or ${circleInput.center} or ${circleInput.key}`);
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
fillStaticKeys.push(circleInput.key);
|
|
145
|
+
const circleForAzimuthCalc = CircleMethods.createCircleClosestAzimuthAngleProperties(circleInput);
|
|
146
|
+
this.circleMap.set(circleInput.key, [circleInput, circleForAzimuthCalc]);
|
|
147
|
+
if (this._staticDynamicStrategy?.getState() !== StaticDynamicState.DYNAMIC) {
|
|
148
|
+
fillStaticKeys.push(circleInput.key);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
this.__buildStaticCircles(fillStaticKeys);
|
|
152
|
+
this.globe.DrawRender();
|
|
153
|
+
}
|
|
154
|
+
deleteBulk(keys) {
|
|
155
|
+
if (this._isFree)
|
|
156
|
+
return;
|
|
157
|
+
if (!this.globe || !this.gl || !this.lineProgram || !this.bufferOrchestrator ||
|
|
158
|
+
!this.bufferManagerMap || !this._vao || !this._circleUBOHandler) {
|
|
159
|
+
throw new Error("Plugin not initialized properly");
|
|
160
|
+
}
|
|
161
|
+
this.bufferOrchestrator.deleteBulk(keys, this.bufferManagerMap);
|
|
162
|
+
for (const key of keys) {
|
|
163
|
+
if (this.circleMap.has(key)) {
|
|
164
|
+
this.circleMap.delete(key);
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
console.warn(`CircleOnTerrainPlugin: Circle ${key} not found in circleMap. Cannot delete.`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
setPluginOpacity(opacity) {
|
|
172
|
+
if (!this.globe || !this.gl) {
|
|
173
|
+
console.warn("Globe or WebGL context is not initialized.");
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
opacityCheck(opacity);
|
|
177
|
+
this._opacity = opacity;
|
|
178
|
+
this.globe.DrawRender();
|
|
179
|
+
}
|
|
180
|
+
// IMPLICIT METHODS
|
|
71
181
|
_buildCircles() {
|
|
72
|
-
//
|
|
73
182
|
// @ts-ignore
|
|
74
|
-
this.__buildCircles();
|
|
75
|
-
return;
|
|
76
183
|
this._staticDynamicStrategy?.updateState();
|
|
77
184
|
const state = this._staticDynamicStrategy?.getState();
|
|
78
|
-
if (state === StaticDynamicState.TO_STATIC)
|
|
79
|
-
console.log("state", state);
|
|
80
185
|
if (state === StaticDynamicState.TO_STATIC) {
|
|
81
186
|
this.__buildStaticCircles();
|
|
82
187
|
}
|
|
@@ -90,11 +195,12 @@ export class CircleOnTerrainPlugin {
|
|
|
90
195
|
!bufferOrchestrator || !bufferManagerMap || !circleMap)
|
|
91
196
|
throw new Error("Plugin not initialized properly");
|
|
92
197
|
// unoptimized.. resets all circles.
|
|
93
|
-
bufferOrchestrator.resetWithCapacity(bufferManagerMap, circleMap.size);
|
|
198
|
+
// bufferOrchestrator.resetWithCapacity(bufferManagerMap, circleMap.size);
|
|
94
199
|
// Prepare the data for the buffers
|
|
95
200
|
const data = [{
|
|
96
201
|
key: "",
|
|
97
|
-
longLatArr: []
|
|
202
|
+
longLatArr: [],
|
|
203
|
+
height: null
|
|
98
204
|
}];
|
|
99
205
|
const lookAtPosition = _cameraUniformBlock.getLookAtVector();
|
|
100
206
|
const cameraPosition = _cameraUniformBlock.getNormalizedCameraVector();
|
|
@@ -102,13 +208,11 @@ export class CircleOnTerrainPlugin {
|
|
|
102
208
|
vec3.add(cameraPosition, cameraPosition, lookAtPosition);
|
|
103
209
|
vec3.normalize(cameraPosition, cameraPosition);
|
|
104
210
|
const currentLOD = globe.api_GetCurrentLODWithDecimal();
|
|
105
|
-
// const currentGeom = globe.api_GetCurrentGeometry();
|
|
106
211
|
const circlePointsLongLat = new Float64Array((CIRCLE_POINTS_COUNT) * 2);
|
|
107
212
|
for (const [key, circle] of this.circleMap.entries()) {
|
|
108
|
-
const [{ radius, center }, circleForAzimuthCalc] = circle;
|
|
213
|
+
const [{ radius, center, height = null }, circleForAzimuthCalc] = circle;
|
|
109
214
|
const closestAzimuthAngle = CircleMethods.closestAzimuthAngle(circleForAzimuthCalc, cameraPosition);
|
|
110
215
|
const stregthLevel = defineStregthLevel(currentLOD, radius);
|
|
111
|
-
// console.log('DrawStregthLevel', stregthLevel, currentLOD, radius);
|
|
112
216
|
if (stregthLevel < 0) {
|
|
113
217
|
console.warn(`CircleOnTerrainPlugin: Circle ${key} has too small radius for current LOD ${currentLOD}. Skipping., radius: ${radius}, stregthLevel: ${stregthLevel}`);
|
|
114
218
|
continue;
|
|
@@ -117,8 +221,9 @@ export class CircleOnTerrainPlugin {
|
|
|
117
221
|
CircleCDF.globeFindPointByPolarHalfCircle(circlePointsLongLat, globe, center[0], center[1], radius, closestAzimuthAngle, templateAngles);
|
|
118
222
|
data[0].key = key;
|
|
119
223
|
data[0].longLatArr = circlePointsLongLat;
|
|
224
|
+
data[0].height = height;
|
|
120
225
|
// Add to buffer orchestrator
|
|
121
|
-
this.bufferOrchestrator.insertBulk(data, bufferManagerMap);
|
|
226
|
+
this.bufferOrchestrator.insertBulk(data, bufferManagerMap, ["position3d", "position2d"]);
|
|
122
227
|
}
|
|
123
228
|
}
|
|
124
229
|
// this will be used to build static circles, which are not affected by camera position or LOD.
|
|
@@ -130,7 +235,9 @@ export class CircleOnTerrainPlugin {
|
|
|
130
235
|
throw new Error("Plugin not initialized properly");
|
|
131
236
|
const data = [{
|
|
132
237
|
key: "",
|
|
133
|
-
longLatArr: []
|
|
238
|
+
longLatArr: [],
|
|
239
|
+
height: null,
|
|
240
|
+
color: [1, 1, 1, 1]
|
|
134
241
|
}];
|
|
135
242
|
// ensure buffer orchestrotrator have enough capacity
|
|
136
243
|
// all circles are build with even sampling, AttractionLevel = 0
|
|
@@ -140,10 +247,12 @@ export class CircleOnTerrainPlugin {
|
|
|
140
247
|
const circlePointsLongLat = new Float64Array((CIRCLE_POINTS_COUNT) * 2);
|
|
141
248
|
bufferOrchestrator.resetWithCapacity(bufferManagerMap, circleMap.size);
|
|
142
249
|
for (const [key, circle] of this.circleMap.entries()) {
|
|
143
|
-
const [{ radius, center }, _] = circle;
|
|
250
|
+
const [{ radius, center, height = null, color = null }, _] = circle;
|
|
144
251
|
CircleCDF.globeFindPointByPolarHalfCircle(circlePointsLongLat, globe, center[0], center[1], radius, zeroRotation, templateAngles);
|
|
145
252
|
data[0].key = key;
|
|
146
253
|
data[0].longLatArr = circlePointsLongLat;
|
|
254
|
+
data[0].height = height;
|
|
255
|
+
data[0].color = color || this._styleOptions.defaultColor;
|
|
147
256
|
this.bufferOrchestrator.insertBulk(data, bufferManagerMap);
|
|
148
257
|
}
|
|
149
258
|
}
|
|
@@ -157,66 +266,18 @@ export class CircleOnTerrainPlugin {
|
|
|
157
266
|
console.warn(`CircleOnTerrainPlugin: Circle ${key} not found in circleMap.`);
|
|
158
267
|
continue;
|
|
159
268
|
}
|
|
160
|
-
const [{ radius, center }, _] = this.circleMap.get(key);
|
|
269
|
+
const [{ radius, center, height = null, color = null }, _] = this.circleMap.get(key);
|
|
161
270
|
const circlePointsLongLat = new Float64Array((CIRCLE_POINTS_COUNT) * 2);
|
|
162
271
|
CircleCDF.globeFindPointByPolarHalfCircle(circlePointsLongLat, globe, center[0], center[1], radius, zeroRotation, templateAngles);
|
|
163
272
|
data[0].key = key;
|
|
164
273
|
data[0].longLatArr = circlePointsLongLat;
|
|
274
|
+
data[0].height = height;
|
|
275
|
+
data[0].color = color || this._styleOptions.defaultColor;
|
|
165
276
|
this.bufferOrchestrator.insertBulk(data, bufferManagerMap);
|
|
166
277
|
}
|
|
167
278
|
}
|
|
168
279
|
}
|
|
169
|
-
|
|
170
|
-
this.globe = globe;
|
|
171
|
-
this.gl = gl;
|
|
172
|
-
// Initialize the program cache for line strip rendering.
|
|
173
|
-
this._staticDynamicStrategy = new StaticDynamicStrategy(globe, 8);
|
|
174
|
-
this.lineProgram = LineStripProgramCache.get(globe);
|
|
175
|
-
const g3D = globe3Dcoordinates(globe, 30);
|
|
176
|
-
const g2D = globe2Dcoordinates(globe);
|
|
177
|
-
this.bufferManagerMap.set("position3d", {
|
|
178
|
-
bufferManager: new BufferManager(gl, (CIRCLE_POINTS_COUNT + 1) * 3), // plus 1 is for butting linestrips
|
|
179
|
-
adaptor: (item) => {
|
|
180
|
-
const { longLatArr } = item;
|
|
181
|
-
const result = g3D(longLatArr, { paddingCount: 1, paddingValue: NaN });
|
|
182
|
-
return result;
|
|
183
|
-
}
|
|
184
|
-
});
|
|
185
|
-
this.bufferManagerMap.set("position2d", {
|
|
186
|
-
bufferManager: new BufferManager(gl, (CIRCLE_POINTS_COUNT + 1) * 2),
|
|
187
|
-
adaptor: (item) => {
|
|
188
|
-
const { longLatArr } = item;
|
|
189
|
-
const result = g2D(longLatArr, { paddingCount: 1, paddingValue: NaN });
|
|
190
|
-
return result;
|
|
191
|
-
}
|
|
192
|
-
});
|
|
193
|
-
this._vao = this.lineProgram.createVAO(createBufferAndReadInfo(this.bufferManagerMap.get("position3d")?.bufferManager.buffer), createBufferAndReadInfo(this.bufferManagerMap.get("position2d")?.bufferManager.buffer), null, null, null);
|
|
194
|
-
this._circleUBOHandler = this.lineProgram.createUBO();
|
|
195
|
-
this._cameraUniformBlock = CameraUniformBlockTotemCache.get(globe);
|
|
196
|
-
}
|
|
197
|
-
insertCircle(key, longitude, latitude, radius) {
|
|
198
|
-
const circle = {
|
|
199
|
-
center: [longitude, latitude],
|
|
200
|
-
radius: radius
|
|
201
|
-
};
|
|
202
|
-
const circleForAzimuthCalc = CircleMethods.createCircleClosestAzimuthAngleProperties(circle);
|
|
203
|
-
this.circleMap.set(key, [circle, circleForAzimuthCalc]);
|
|
204
|
-
// @ts-ignore
|
|
205
|
-
if (this._staticDynamicStrategy.getState() === StaticDynamicState.STATIC) {
|
|
206
|
-
this.__buildStaticCircles([key]);
|
|
207
|
-
}
|
|
208
|
-
this.globe?.DrawRender();
|
|
209
|
-
}
|
|
210
|
-
deleteCircle(keys) {
|
|
211
|
-
for (const key of keys) {
|
|
212
|
-
if (this.circleMap.has(key)) {
|
|
213
|
-
this.circleMap.delete(key);
|
|
214
|
-
this.globe?.DrawRender();
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
this._dobuild = true;
|
|
218
|
-
this.globe?.DrawRender();
|
|
219
|
-
}
|
|
280
|
+
// GLOBE API INTERFACE
|
|
220
281
|
draw3D() {
|
|
221
282
|
const { _isFree, globe, gl, lineProgram, bufferOrchestrator, bufferManagerMap, _vao, _circleUBOHandler } = this;
|
|
222
283
|
if (_isFree || !globe || !gl || !lineProgram || !bufferOrchestrator ||
|