@pirireis/webglobeplugins 0.15.17-alpha → 0.15.19-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/methods.js +2 -2
- package/package.json +1 -1
- package/point-tracks/plugin.js +21 -16
- package/programs/line-on-globe/linestrip/linestrip.js +0 -1
- package/programs/totems/globe-changes.js +52 -0
- package/range-tools-on-terrain/bearing-line/adapters.js +8 -5
- package/range-tools-on-terrain/bearing-line/plugin.js +6 -4
- package/range-tools-on-terrain/circle-line-chain/adapters.js +2 -0
- package/range-tools-on-terrain/circle-line-chain/plugin.js +15 -4
- package/range-tools-on-terrain/range-ring/adapters.js +59 -7
- package/range-tools-on-terrain/range-ring/plugin.js +23 -3
- package/semiplugins/lightweight/line-plugin.js +8 -4
- package/semiplugins/lightweight/piece-of-pie-plugin.js +4 -2
- package/semiplugins/shape-on-terrain/arc-plugin.js +34 -10
- package/semiplugins/shape-on-terrain/circle-plugin.js +46 -21
- package/semiplugins/shape-on-terrain/padding-1-degree.js +357 -190
- package/util/account/single-attribute-buffer-management/buffer-manager.js +10 -0
- package/util/account/single-attribute-buffer-management/buffer-orchestrator copy.js +161 -0
- package/util/account/single-attribute-buffer-management/buffer-orchestrator.js +91 -5
- package/util/account/single-attribute-buffer-management/chunked-buffer-manager.js +75 -0
- package/util/account/single-attribute-buffer-management/chunked-buffer-orchestrator.js +195 -0
- package/util/account/single-attribute-buffer-management/object-store.js +7 -0
- package/util/check/typecheck.js +12 -0
- package/util/geometry/index.js +2 -1
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
const EXTRA_SIZE = 10;
|
|
2
|
+
export class BufferOrchestrator {
|
|
3
|
+
_capacity;
|
|
4
|
+
offsetMap;
|
|
5
|
+
tombstoneOffsets;
|
|
6
|
+
_length;
|
|
7
|
+
constructor({ capacity = 10 } = {}) {
|
|
8
|
+
this._capacity = capacity;
|
|
9
|
+
this.offsetMap = new Map();
|
|
10
|
+
this.tombstoneOffsets = [];
|
|
11
|
+
this._length = 0;
|
|
12
|
+
}
|
|
13
|
+
resetWithCapacity(bufferManagersMap, capacity = null) {
|
|
14
|
+
this._capacity = capacity !== null ? capacity : this._capacity;
|
|
15
|
+
for (const [key, { bufferManager }] of bufferManagersMap) {
|
|
16
|
+
bufferManager.resetWithCapacity(this._capacity);
|
|
17
|
+
}
|
|
18
|
+
this.offsetMap.clear();
|
|
19
|
+
this.tombstoneOffsets = [];
|
|
20
|
+
this._length = 0;
|
|
21
|
+
}
|
|
22
|
+
insertBulk(items, bufferManagersMap, bufferKeys = null) {
|
|
23
|
+
this.ensureSpace(items.length, bufferManagersMap);
|
|
24
|
+
const { offsetMap } = this;
|
|
25
|
+
const offsets = [];
|
|
26
|
+
for (const item of items) {
|
|
27
|
+
let o = offsetMap.get(item.key);
|
|
28
|
+
const offset = o !== undefined ? o : this.nextOffset();
|
|
29
|
+
offsetMap.set(item.key, offset);
|
|
30
|
+
offsets.push(offset);
|
|
31
|
+
}
|
|
32
|
+
if (bufferKeys) {
|
|
33
|
+
for (const key of bufferKeys) {
|
|
34
|
+
const bufferManagerComp = bufferManagersMap.get(key);
|
|
35
|
+
if (bufferManagerComp === undefined)
|
|
36
|
+
throw new Error("insertBulk bufferKey does not exist");
|
|
37
|
+
const { bufferManager, adaptor } = bufferManagerComp;
|
|
38
|
+
bufferManager.insertBulk(items.map(adaptor), offsets);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
for (const [key, { bufferManager, adaptor }] of bufferManagersMap) {
|
|
43
|
+
bufferManager.insertBulk(items.map(adaptor), offsets);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
// does not assign offset to the new items.
|
|
48
|
+
updateBulk(items, bufferManagersMap, bufferKeys = null) {
|
|
49
|
+
const { offsetMap } = this;
|
|
50
|
+
const offsets = [];
|
|
51
|
+
for (const item of items) {
|
|
52
|
+
const offset = offsetMap.get(item.key);
|
|
53
|
+
if (offset !== undefined) {
|
|
54
|
+
offsets.push(offset);
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
throw new Error("updateBulk item Key does not exist");
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (bufferKeys) {
|
|
61
|
+
for (const key of bufferKeys) {
|
|
62
|
+
const bufferManagerComp = bufferManagersMap.get(key);
|
|
63
|
+
if (bufferManagerComp === undefined)
|
|
64
|
+
throw new Error("updateBulk bufferKey does not exist");
|
|
65
|
+
const { bufferManager, adaptor } = bufferManagerComp;
|
|
66
|
+
bufferManager.insertBulk(items.map(adaptor), offsets);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
for (const [key, { bufferManager, adaptor }] of bufferManagersMap) {
|
|
71
|
+
bufferManager.insertBulk(items.map(adaptor), offsets);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
deleteBulk(keys, bufferManagersMap) {
|
|
76
|
+
const offsets = [];
|
|
77
|
+
for (const key of keys) {
|
|
78
|
+
const offset = this.getOffset(key);
|
|
79
|
+
if (offset !== undefined) {
|
|
80
|
+
offsets.push(offset);
|
|
81
|
+
this.offsetMap.delete(key);
|
|
82
|
+
this.tombstoneOffsets.push(offset);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
for (const [key, { bufferManager }] of bufferManagersMap) {
|
|
86
|
+
bufferManager.deleteBulk(offsets);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
getOffset(key) {
|
|
90
|
+
return this.offsetMap.get(key);
|
|
91
|
+
}
|
|
92
|
+
nextOffset() {
|
|
93
|
+
if (this.tombstoneOffsets.length > 0) {
|
|
94
|
+
const offset = this.tombstoneOffsets.pop();
|
|
95
|
+
return offset;
|
|
96
|
+
}
|
|
97
|
+
if (this._length < this._capacity) {
|
|
98
|
+
return this._length++;
|
|
99
|
+
}
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
ensureSpace(itemsLength, bufferManagersMap) {
|
|
103
|
+
if (itemsLength <= this.emptySpace)
|
|
104
|
+
return;
|
|
105
|
+
const newCapacity = this.length + itemsLength;
|
|
106
|
+
for (const [key, { bufferManager }] of bufferManagersMap) {
|
|
107
|
+
bufferManager.extendBuffer(this.length, newCapacity);
|
|
108
|
+
}
|
|
109
|
+
this._capacity = newCapacity;
|
|
110
|
+
}
|
|
111
|
+
defrag(bufferManagers, bufferKeys) {
|
|
112
|
+
const offsetMap = this.offsetMap;
|
|
113
|
+
const newCapacity = offsetMap.size + EXTRA_SIZE;
|
|
114
|
+
if (bufferKeys) {
|
|
115
|
+
for (const key of bufferKeys) {
|
|
116
|
+
const offset = offsetMap.get(key);
|
|
117
|
+
if (offset !== undefined) {
|
|
118
|
+
for (const [key, { bufferManager }] of bufferManagers) {
|
|
119
|
+
bufferManager.defrag([offset], this.length, newCapacity);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
for (const [key, { bufferManager }] of bufferManagers) {
|
|
126
|
+
bufferManager.defrag(offsetMap.values(), this.length, newCapacity);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
this._defrag();
|
|
130
|
+
this._length = offsetMap.size;
|
|
131
|
+
this._capacity = newCapacity;
|
|
132
|
+
this.tombstoneOffsets = [];
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Flushes metadata and sets length to 0 without actualize change on buffers
|
|
136
|
+
* This method created for cases in which data is loaded on each frame
|
|
137
|
+
*/
|
|
138
|
+
flush({ capacity = 10 } = {}) {
|
|
139
|
+
this._length = 0;
|
|
140
|
+
this._capacity = capacity;
|
|
141
|
+
this.tombstoneOffsets = [];
|
|
142
|
+
this.offsetMap.clear();
|
|
143
|
+
}
|
|
144
|
+
_defrag() {
|
|
145
|
+
const newOffsetMap = new Map();
|
|
146
|
+
let newOffset = 0;
|
|
147
|
+
for (const [key, offset] of this.offsetMap) {
|
|
148
|
+
newOffsetMap.set(key, newOffset++);
|
|
149
|
+
}
|
|
150
|
+
this.offsetMap = newOffsetMap;
|
|
151
|
+
}
|
|
152
|
+
get length() {
|
|
153
|
+
return this._length;
|
|
154
|
+
}
|
|
155
|
+
get emptySpace() {
|
|
156
|
+
return this._capacity - this.offsetMap.size;
|
|
157
|
+
}
|
|
158
|
+
get capacity() {
|
|
159
|
+
return this._capacity;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -19,28 +19,114 @@ export class BufferOrchestrator {
|
|
|
19
19
|
this.tombstoneOffsets = [];
|
|
20
20
|
this._length = 0;
|
|
21
21
|
}
|
|
22
|
+
// want to add stack load to this method
|
|
23
|
+
// if offsetMap.has(item.key) === false get next offset, add the item to stack
|
|
24
|
+
// create a single big float32array and fill it with the items
|
|
25
|
+
// bufferManager.insertBulk([bigFloat32Array], [offsets[0]])
|
|
22
26
|
insertBulk(items, bufferManagersMap, bufferKeys = null) {
|
|
23
27
|
this.ensureSpace(items.length, bufferManagersMap);
|
|
24
28
|
const { offsetMap } = this;
|
|
25
|
-
|
|
29
|
+
let offsetStart = null;
|
|
30
|
+
const itemForConsecutiveLoad = [];
|
|
26
31
|
for (const item of items) {
|
|
27
32
|
let o = offsetMap.get(item.key);
|
|
28
|
-
|
|
33
|
+
if (o !== undefined) {
|
|
34
|
+
if (bufferKeys) {
|
|
35
|
+
for (const key of bufferKeys) {
|
|
36
|
+
const bufferManagerComp = bufferManagersMap.get(key);
|
|
37
|
+
if (bufferManagerComp === undefined)
|
|
38
|
+
throw new Error("insertBulk bufferKey does not exist");
|
|
39
|
+
const { bufferManager, adaptor } = bufferManagerComp;
|
|
40
|
+
bufferManager.insertBulk([adaptor(item)], [o]);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
for (const [key, { bufferManager, adaptor }] of bufferManagersMap) {
|
|
45
|
+
bufferManager.insertBulk([adaptor(item)], [o]);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const offset = this.nextOffset();
|
|
29
51
|
offsetMap.set(item.key, offset);
|
|
30
|
-
|
|
52
|
+
itemForConsecutiveLoad.push(item);
|
|
53
|
+
if (offsetStart === null) {
|
|
54
|
+
offsetStart = offset;
|
|
55
|
+
}
|
|
31
56
|
}
|
|
57
|
+
if (offsetStart === null)
|
|
58
|
+
return;
|
|
32
59
|
if (bufferKeys) {
|
|
33
60
|
for (const key of bufferKeys) {
|
|
34
61
|
const bufferManagerComp = bufferManagersMap.get(key);
|
|
35
62
|
if (bufferManagerComp === undefined)
|
|
36
63
|
throw new Error("insertBulk bufferKey does not exist");
|
|
37
64
|
const { bufferManager, adaptor } = bufferManagerComp;
|
|
38
|
-
bufferManager.
|
|
65
|
+
bufferManager.insertConsecutiveBulk(itemForConsecutiveLoad, offsetStart, adaptor, Float32Array);
|
|
39
66
|
}
|
|
40
67
|
}
|
|
41
68
|
else {
|
|
42
69
|
for (const [key, { bufferManager, adaptor }] of bufferManagersMap) {
|
|
43
|
-
bufferManager.
|
|
70
|
+
bufferManager.insertConsecutiveBulk(itemForConsecutiveLoad, offsetStart, adaptor, Float32Array);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
insertBulk2(items, bufferManagersMap, bufferKeys = null) {
|
|
75
|
+
this.ensureSpace(items.length, bufferManagersMap);
|
|
76
|
+
const { offsetMap } = this;
|
|
77
|
+
// Pre-allocate arrays only if needed
|
|
78
|
+
const newItemsCount = items.reduce((acc, item) => acc + (offsetMap.has(item.key) ? 0 : 1), 0);
|
|
79
|
+
if (newItemsCount === 0)
|
|
80
|
+
return;
|
|
81
|
+
// Only allocate if there are new items
|
|
82
|
+
const newItems = new Array(newItemsCount);
|
|
83
|
+
const newOffsets = new Array(newItemsCount);
|
|
84
|
+
let idx = 0;
|
|
85
|
+
for (const item of items) {
|
|
86
|
+
let o = offsetMap.get(item.key);
|
|
87
|
+
if (o !== undefined) {
|
|
88
|
+
// Update existing
|
|
89
|
+
if (bufferKeys) {
|
|
90
|
+
for (const key of bufferKeys) {
|
|
91
|
+
const bufferManagerComp = bufferManagersMap.get(key);
|
|
92
|
+
if (!bufferManagerComp)
|
|
93
|
+
throw new Error("insertBulk bufferKey does not exist");
|
|
94
|
+
const { bufferManager, adaptor } = bufferManagerComp;
|
|
95
|
+
bufferManager.insertBulk([adaptor(item)], [o]);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
for (const [key, { bufferManager, adaptor }] of bufferManagersMap) {
|
|
100
|
+
bufferManager.insertBulk([adaptor(item)], [o]);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
// New item
|
|
106
|
+
const offset = this.nextOffset();
|
|
107
|
+
offsetMap.set(item.key, offset);
|
|
108
|
+
newItems[idx] = item;
|
|
109
|
+
newOffsets[idx] = offset;
|
|
110
|
+
idx++;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
if (idx > 0) {
|
|
114
|
+
// Only slice the arrays to the actual number of new items
|
|
115
|
+
const itemsToInsert = newItems.slice(0, idx);
|
|
116
|
+
const offsetsToInsert = newOffsets.slice(0, idx);
|
|
117
|
+
if (bufferKeys) {
|
|
118
|
+
for (const key of bufferKeys) {
|
|
119
|
+
const bufferManagerComp = bufferManagersMap.get(key);
|
|
120
|
+
if (!bufferManagerComp)
|
|
121
|
+
throw new Error("insertBulk bufferKey does not exist");
|
|
122
|
+
const { bufferManager, adaptor } = bufferManagerComp;
|
|
123
|
+
bufferManager.insertConsecutiveBulk(itemsToInsert, offsetsToInsert[0], adaptor, Float32Array);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
for (const [key, { bufferManager, adaptor }] of bufferManagersMap) {
|
|
128
|
+
bufferManager.insertConsecutiveBulk(itemsToInsert, offsetsToInsert[0], adaptor, Float32Array);
|
|
129
|
+
}
|
|
44
130
|
}
|
|
45
131
|
}
|
|
46
132
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { BufferManager } from "./buffer-manager";
|
|
2
|
+
export class ChunkedBufferManager extends BufferManager {
|
|
3
|
+
// Chunked insert that batches multiple items into fewer GPU calls
|
|
4
|
+
insertBulkChunked(blocks, offsets, chunkSize = 1000) {
|
|
5
|
+
const { gl, buffer, itemSize } = this;
|
|
6
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
7
|
+
const offsetMultiplier = itemSize * 4;
|
|
8
|
+
// Process in chunks to reduce GPU call overhead
|
|
9
|
+
for (let i = 0; i < blocks.length; i += chunkSize) {
|
|
10
|
+
const chunkEnd = Math.min(i + chunkSize, blocks.length);
|
|
11
|
+
const chunkBlocks = blocks.slice(i, chunkEnd);
|
|
12
|
+
const chunkOffsets = offsets.slice(i, chunkEnd);
|
|
13
|
+
// Create a single large buffer for this chunk
|
|
14
|
+
const totalItems = chunkBlocks.length;
|
|
15
|
+
const chunkBuffer = new Float32Array(totalItems * itemSize);
|
|
16
|
+
// Copy all chunk data into one buffer
|
|
17
|
+
for (let j = 0; j < chunkBlocks.length; j++) {
|
|
18
|
+
chunkBuffer.set(chunkBlocks[j], j * itemSize);
|
|
19
|
+
}
|
|
20
|
+
// Find contiguous ranges for efficient uploads
|
|
21
|
+
const ranges = this.findContiguousRanges(chunkOffsets, offsetMultiplier);
|
|
22
|
+
for (const range of ranges) {
|
|
23
|
+
const startOffset = range.startOffset;
|
|
24
|
+
const dataStart = range.indices[0] * itemSize;
|
|
25
|
+
const dataLength = range.indices.length * itemSize;
|
|
26
|
+
const rangeData = chunkBuffer.subarray(dataStart, dataStart + dataLength);
|
|
27
|
+
gl.bufferSubData(gl.ARRAY_BUFFER, startOffset, rangeData);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
|
31
|
+
}
|
|
32
|
+
// Optimized delete that batches operations
|
|
33
|
+
deleteBulkChunked(offsets, chunkSize = 1000) {
|
|
34
|
+
const { gl, buffer, itemSize } = this;
|
|
35
|
+
const emptyBlock = new Float32Array(itemSize).fill(NaN);
|
|
36
|
+
const offsetMultiplier = itemSize * 4;
|
|
37
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
38
|
+
// Sort offsets for better cache coherency
|
|
39
|
+
const sortedOffsets = [...offsets].sort((a, b) => a - b);
|
|
40
|
+
// Process in chunks
|
|
41
|
+
for (let i = 0; i < sortedOffsets.length; i += chunkSize) {
|
|
42
|
+
const chunkEnd = Math.min(i + chunkSize, sortedOffsets.length);
|
|
43
|
+
for (let j = i; j < chunkEnd; j++) {
|
|
44
|
+
const offset = sortedOffsets[j];
|
|
45
|
+
if (offset !== undefined) {
|
|
46
|
+
gl.bufferSubData(gl.ARRAY_BUFFER, offset * offsetMultiplier, emptyBlock);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, null);
|
|
51
|
+
}
|
|
52
|
+
// Find contiguous memory ranges for batch uploads
|
|
53
|
+
findContiguousRanges(offsets, offsetMultiplier) {
|
|
54
|
+
const ranges = [];
|
|
55
|
+
const sortedIndices = offsets
|
|
56
|
+
.map((offset, index) => ({ offset: offset * offsetMultiplier, index }))
|
|
57
|
+
.sort((a, b) => a.offset - b.offset);
|
|
58
|
+
let currentRange = null;
|
|
59
|
+
for (const { offset, index } of sortedIndices) {
|
|
60
|
+
if (!currentRange || offset !== currentRange.startOffset + currentRange.indices.length * offsetMultiplier) {
|
|
61
|
+
// Start new range
|
|
62
|
+
if (currentRange)
|
|
63
|
+
ranges.push(currentRange);
|
|
64
|
+
currentRange = { startOffset: offset, indices: [index] };
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
// Extend current range
|
|
68
|
+
currentRange.indices.push(index);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (currentRange)
|
|
72
|
+
ranges.push(currentRange);
|
|
73
|
+
return ranges;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
export class ChunkedBufferOrchestrator {
|
|
2
|
+
_capacity;
|
|
3
|
+
_length;
|
|
4
|
+
offsetMap;
|
|
5
|
+
tombstoneOffsets;
|
|
6
|
+
// Chunking configuration
|
|
7
|
+
chunkSize;
|
|
8
|
+
maxPendingOperations;
|
|
9
|
+
autoFlushInterval;
|
|
10
|
+
// Pending operations batching
|
|
11
|
+
pendingInserts;
|
|
12
|
+
pendingUpdates;
|
|
13
|
+
pendingDeletes;
|
|
14
|
+
operationCount;
|
|
15
|
+
flushTimer;
|
|
16
|
+
constructor({ capacity = 10, chunkSize = 1000, maxPendingOperations = 5000, autoFlushInterval = 16 // ~60fps
|
|
17
|
+
} = {}) {
|
|
18
|
+
this._capacity = capacity;
|
|
19
|
+
this._length = 0;
|
|
20
|
+
this.offsetMap = new Map();
|
|
21
|
+
this.tombstoneOffsets = [];
|
|
22
|
+
this.chunkSize = chunkSize;
|
|
23
|
+
this.maxPendingOperations = maxPendingOperations;
|
|
24
|
+
this.autoFlushInterval = autoFlushInterval;
|
|
25
|
+
this.pendingInserts = new Map();
|
|
26
|
+
this.pendingUpdates = new Map();
|
|
27
|
+
this.pendingDeletes = new Set();
|
|
28
|
+
this.operationCount = 0;
|
|
29
|
+
this.flushTimer = null;
|
|
30
|
+
this.startAutoFlush();
|
|
31
|
+
}
|
|
32
|
+
resetWithCapacity(bufferManagersMap, capacity = null) {
|
|
33
|
+
this.flushPendingOperations(bufferManagersMap);
|
|
34
|
+
this._capacity = capacity !== null ? capacity : this._capacity;
|
|
35
|
+
for (const [key, { bufferManager }] of bufferManagersMap) {
|
|
36
|
+
bufferManager.resetWithCapacity(this._capacity);
|
|
37
|
+
}
|
|
38
|
+
this.offsetMap.clear();
|
|
39
|
+
this.tombstoneOffsets = [];
|
|
40
|
+
this._length = 0;
|
|
41
|
+
}
|
|
42
|
+
// Batch insert operations
|
|
43
|
+
insertBulk(items, bufferManagersMap, bufferKeys = null) {
|
|
44
|
+
const targetKeys = bufferKeys || Array.from(bufferManagersMap.keys());
|
|
45
|
+
// Process items in chunks
|
|
46
|
+
for (let i = 0; i < items.length; i += this.chunkSize) {
|
|
47
|
+
const chunk = items.slice(i, i + this.chunkSize);
|
|
48
|
+
this.queueInsertChunk(chunk, targetKeys);
|
|
49
|
+
}
|
|
50
|
+
this.operationCount += items.length;
|
|
51
|
+
this.checkAutoFlush(bufferManagersMap);
|
|
52
|
+
}
|
|
53
|
+
// Batch update operations
|
|
54
|
+
updateBulk(items, bufferManagersMap, bufferKeys = null) {
|
|
55
|
+
const targetKeys = bufferKeys || Array.from(bufferManagersMap.keys());
|
|
56
|
+
for (let i = 0; i < items.length; i += this.chunkSize) {
|
|
57
|
+
const chunk = items.slice(i, i + this.chunkSize);
|
|
58
|
+
this.queueUpdateChunk(chunk, targetKeys);
|
|
59
|
+
}
|
|
60
|
+
this.operationCount += items.length;
|
|
61
|
+
this.checkAutoFlush(bufferManagersMap);
|
|
62
|
+
}
|
|
63
|
+
// Batch delete operations
|
|
64
|
+
deleteBulk(keys, bufferManagersMap) {
|
|
65
|
+
for (const key of keys) {
|
|
66
|
+
const offset = this.offsetMap.get(key);
|
|
67
|
+
if (offset !== undefined) {
|
|
68
|
+
this.pendingDeletes.add(offset);
|
|
69
|
+
this.offsetMap.delete(key);
|
|
70
|
+
this.tombstoneOffsets.push(offset);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
this.operationCount += keys.length;
|
|
74
|
+
this.checkAutoFlush(bufferManagersMap);
|
|
75
|
+
}
|
|
76
|
+
queueInsertChunk(items, bufferKeys) {
|
|
77
|
+
this.ensureSpaceForItems(items.length);
|
|
78
|
+
const offsets = [];
|
|
79
|
+
for (const item of items) {
|
|
80
|
+
let offset = this.offsetMap.get(item.key);
|
|
81
|
+
if (offset === undefined) {
|
|
82
|
+
offset = this.nextOffset();
|
|
83
|
+
this.offsetMap.set(item.key, offset);
|
|
84
|
+
}
|
|
85
|
+
offsets.push(offset);
|
|
86
|
+
}
|
|
87
|
+
for (const bufferKey of bufferKeys) {
|
|
88
|
+
if (!this.pendingInserts.has(bufferKey)) {
|
|
89
|
+
this.pendingInserts.set(bufferKey, { items: [], offsets: [] });
|
|
90
|
+
}
|
|
91
|
+
const pending = this.pendingInserts.get(bufferKey);
|
|
92
|
+
pending.items.push(...items);
|
|
93
|
+
pending.offsets.push(...offsets);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
queueUpdateChunk(items, bufferKeys) {
|
|
97
|
+
const offsets = [];
|
|
98
|
+
for (const item of items) {
|
|
99
|
+
const offset = this.offsetMap.get(item.key);
|
|
100
|
+
if (offset === undefined) {
|
|
101
|
+
throw new Error(`Update item key '${item.key}' does not exist`);
|
|
102
|
+
}
|
|
103
|
+
offsets.push(offset);
|
|
104
|
+
}
|
|
105
|
+
for (const bufferKey of bufferKeys) {
|
|
106
|
+
if (!this.pendingUpdates.has(bufferKey)) {
|
|
107
|
+
this.pendingUpdates.set(bufferKey, { items: [], offsets: [] });
|
|
108
|
+
}
|
|
109
|
+
const pending = this.pendingUpdates.get(bufferKey);
|
|
110
|
+
pending.items.push(...items);
|
|
111
|
+
pending.offsets.push(...offsets);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
checkAutoFlush(bufferManagersMap) {
|
|
115
|
+
if (this.operationCount >= this.maxPendingOperations) {
|
|
116
|
+
this.flushPendingOperations(bufferManagersMap);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// Force flush all pending operations to GPU
|
|
120
|
+
flushPendingOperations(bufferManagersMap) {
|
|
121
|
+
// Process deletes first
|
|
122
|
+
if (this.pendingDeletes.size > 0) {
|
|
123
|
+
const deleteOffsets = Array.from(this.pendingDeletes);
|
|
124
|
+
for (const [key, { bufferManager }] of bufferManagersMap) {
|
|
125
|
+
bufferManager.deleteBulk(deleteOffsets);
|
|
126
|
+
}
|
|
127
|
+
this.pendingDeletes.clear();
|
|
128
|
+
}
|
|
129
|
+
// Process inserts
|
|
130
|
+
for (const [bufferKey, { items, offsets }] of this.pendingInserts) {
|
|
131
|
+
const bufferManagerComp = bufferManagersMap.get(bufferKey);
|
|
132
|
+
if (bufferManagerComp) {
|
|
133
|
+
const { bufferManager, adaptor } = bufferManagerComp;
|
|
134
|
+
const adaptedData = items.map(adaptor);
|
|
135
|
+
bufferManager.insertBulkChunked(adaptedData, offsets, this.chunkSize);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
this.pendingInserts.clear();
|
|
139
|
+
// Process updates
|
|
140
|
+
for (const [bufferKey, { items, offsets }] of this.pendingUpdates) {
|
|
141
|
+
const bufferManagerComp = bufferManagersMap.get(bufferKey);
|
|
142
|
+
if (bufferManagerComp) {
|
|
143
|
+
const { bufferManager, adaptor } = bufferManagerComp;
|
|
144
|
+
const adaptedData = items.map(adaptor);
|
|
145
|
+
bufferManager.insertBulkChunked(adaptedData, offsets, this.chunkSize);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
this.pendingUpdates.clear();
|
|
149
|
+
this.operationCount = 0;
|
|
150
|
+
}
|
|
151
|
+
ensureSpaceForItems(itemCount) {
|
|
152
|
+
if (itemCount <= this.emptySpace)
|
|
153
|
+
return;
|
|
154
|
+
const newCapacity = Math.max(this._capacity * 2, this._length + itemCount + this.chunkSize);
|
|
155
|
+
this._capacity = newCapacity;
|
|
156
|
+
}
|
|
157
|
+
nextOffset() {
|
|
158
|
+
if (this.tombstoneOffsets.length > 0) {
|
|
159
|
+
return this.tombstoneOffsets.pop();
|
|
160
|
+
}
|
|
161
|
+
if (this._length < this._capacity) {
|
|
162
|
+
return this._length++;
|
|
163
|
+
}
|
|
164
|
+
return false;
|
|
165
|
+
}
|
|
166
|
+
startAutoFlush() {
|
|
167
|
+
if (this.autoFlushInterval > 0) {
|
|
168
|
+
this.flushTimer = setInterval(() => {
|
|
169
|
+
if (this.operationCount > 0) {
|
|
170
|
+
// Note: You'll need to pass bufferManagersMap here
|
|
171
|
+
// This requires restructuring to maintain reference
|
|
172
|
+
console.log(`Auto-flush triggered with ${this.operationCount} pending operations`);
|
|
173
|
+
}
|
|
174
|
+
}, this.autoFlushInterval);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
stopAutoFlush() {
|
|
178
|
+
if (this.flushTimer) {
|
|
179
|
+
clearInterval(this.flushTimer);
|
|
180
|
+
this.flushTimer = null;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
// Getters
|
|
184
|
+
get length() { return this._length; }
|
|
185
|
+
get emptySpace() { return this._capacity - this.offsetMap.size; }
|
|
186
|
+
get capacity() { return this._capacity; }
|
|
187
|
+
get pendingOperationCount() { return this.operationCount; }
|
|
188
|
+
// Manual control
|
|
189
|
+
forceMajorFlush(bufferManagersMap) {
|
|
190
|
+
this.flushPendingOperations(bufferManagersMap);
|
|
191
|
+
}
|
|
192
|
+
getOffset(key) {
|
|
193
|
+
return this.offsetMap.get(key);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
@@ -23,6 +23,13 @@ export class ObjectStore {
|
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
+
insertConsecutiveBulk(objects, startOffset) {
|
|
27
|
+
for (let i = 0; i < objects.length; i++) {
|
|
28
|
+
const object = objects[i];
|
|
29
|
+
const offset = startOffset + i;
|
|
30
|
+
this._container[offset] = object;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
26
33
|
defrag(offsetValues, occupiedCapacity, newCapacity) {
|
|
27
34
|
const hold = this._createEmptyList(newCapacity);
|
|
28
35
|
for (let i = 0; i < offsetValues.length; i++) {
|
package/util/check/typecheck.js
CHANGED
|
@@ -9,6 +9,18 @@ const doesOwnProperties = (properties, errorMessage) => {
|
|
|
9
9
|
};
|
|
10
10
|
export const isHexColor = (hexColor) => /^#[0-9A-F]{6}$/i.test(hexColor);
|
|
11
11
|
export const isHexColorWithOpacity = (hexColor) => /^#[0-9A-F]{6}[0-9a-f]{0,2}$/i.test(hexColor);
|
|
12
|
+
export const colorCheck = (color) => {
|
|
13
|
+
if (Array.isArray(color)) {
|
|
14
|
+
if (color.length !== 4)
|
|
15
|
+
throw new TypeError("Color must be an array 4 numbers RGBA");
|
|
16
|
+
color.forEach(c => {
|
|
17
|
+
if (typeof c !== "number")
|
|
18
|
+
throw new TypeError("Color array must contain only numbers");
|
|
19
|
+
if (c < 0 || c > 1)
|
|
20
|
+
throw new RangeError("Color values must be between 0 and 1");
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
};
|
|
12
24
|
export const opacityCheck = (opacity) => {
|
|
13
25
|
if (typeof opacity !== "number")
|
|
14
26
|
throw new TypeError("style.opacity must a number");
|
package/util/geometry/index.js
CHANGED
|
@@ -40,7 +40,8 @@ function normalize(array, newLength = 1) {
|
|
|
40
40
|
}
|
|
41
41
|
return result;
|
|
42
42
|
}
|
|
43
|
+
// TODO carry the usefull content to math module.
|
|
43
44
|
function sphereCoord(long, lat, globe, height = 0) {
|
|
44
|
-
return normalize(globe.api_GetCartesian3DPoint(long, lat, 0,
|
|
45
|
+
return normalize(globe.api_GetCartesian3DPoint(long, lat, 0, false), 6378.137 + height);
|
|
45
46
|
}
|
|
46
47
|
export { createBBoxMatrix, latLongToPixelXY, latLongBboxtoPixelXYBbox, sphereCoord, normalize };
|