maplibre-copc-layer 0.0.1
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/README.md +124 -0
- package/dist/index.d.mts +176 -0
- package/dist/index.mjs +645 -0
- package/package.json +52 -0
- package/src/cache-manager.ts +184 -0
- package/src/copclayer.ts +704 -0
- package/src/globe-control.ts +70 -0
- package/src/index.ts +13 -0
- package/src/shaders/edl.frag.glsl +49 -0
- package/src/shaders/edl.vert.glsl +6 -0
- package/src/shaders/points.frag.glsl +19 -0
- package/src/shaders/points.vert.glsl +21 -0
- package/src/shaders/shaders.d.ts +4 -0
- package/src/worker/index.ts +347 -0
- package/src/worker/sse.ts +41 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import * as THREE from 'three'
|
|
2
|
+
|
|
3
|
+
export interface CachedNodeData {
|
|
4
|
+
nodeId: string
|
|
5
|
+
positions: Float64Array
|
|
6
|
+
colors: Float32Array
|
|
7
|
+
pointCount: number
|
|
8
|
+
geometry?: THREE.BufferGeometry
|
|
9
|
+
points?: THREE.Points
|
|
10
|
+
materialConfig: {
|
|
11
|
+
colorMode: string
|
|
12
|
+
pointSize: number
|
|
13
|
+
depthTest: boolean
|
|
14
|
+
}
|
|
15
|
+
lastAccessed: number
|
|
16
|
+
sizeBytes: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface CacheManagerOptions {
|
|
20
|
+
maxNodes?: number
|
|
21
|
+
maxMemoryBytes?: number
|
|
22
|
+
debug?: boolean
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class CacheManager {
|
|
26
|
+
private cache = new Map<string, CachedNodeData>()
|
|
27
|
+
private memoryUsage = 0
|
|
28
|
+
private options: Required<CacheManagerOptions>
|
|
29
|
+
|
|
30
|
+
constructor(options: CacheManagerOptions = {}) {
|
|
31
|
+
this.options = {
|
|
32
|
+
maxNodes: options.maxNodes ?? 100,
|
|
33
|
+
maxMemoryBytes: options.maxMemoryBytes ?? 100 * 1024 * 1024,
|
|
34
|
+
debug: options.debug ?? false,
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
get(nodeId: string): CachedNodeData | null {
|
|
39
|
+
const data = this.cache.get(nodeId)
|
|
40
|
+
if (data) {
|
|
41
|
+
// Move to end of Map iteration order (most recently used)
|
|
42
|
+
this.cache.delete(nodeId)
|
|
43
|
+
this.cache.set(nodeId, data)
|
|
44
|
+
data.lastAccessed = Date.now()
|
|
45
|
+
return data
|
|
46
|
+
}
|
|
47
|
+
return null
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
set(nodeData: CachedNodeData, protectedNodes?: Set<string>): void {
|
|
51
|
+
const { nodeId } = nodeData
|
|
52
|
+
const existing = this.cache.get(nodeId)
|
|
53
|
+
if (existing) {
|
|
54
|
+
this.disposeNodeResources(existing)
|
|
55
|
+
this.memoryUsage -= existing.sizeBytes
|
|
56
|
+
this.cache.delete(nodeId)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
this.ensureCacheLimits(nodeData.sizeBytes, protectedNodes)
|
|
60
|
+
|
|
61
|
+
nodeData.lastAccessed = Date.now()
|
|
62
|
+
this.cache.set(nodeId, nodeData)
|
|
63
|
+
this.memoryUsage += nodeData.sizeBytes
|
|
64
|
+
|
|
65
|
+
this.log(`Cached node ${nodeId} (${this.formatBytes(nodeData.sizeBytes)})`)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
has(nodeId: string): boolean {
|
|
69
|
+
return this.cache.has(nodeId)
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
delete(nodeId: string): boolean {
|
|
73
|
+
const data = this.cache.get(nodeId)
|
|
74
|
+
if (!data) return false
|
|
75
|
+
|
|
76
|
+
this.disposeNodeResources(data)
|
|
77
|
+
this.cache.delete(nodeId)
|
|
78
|
+
this.memoryUsage -= data.sizeBytes
|
|
79
|
+
|
|
80
|
+
this.log(`Removed node ${nodeId} from cache`)
|
|
81
|
+
return true
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
clear(): void {
|
|
85
|
+
for (const data of this.cache.values()) {
|
|
86
|
+
this.disposeNodeResources(data)
|
|
87
|
+
}
|
|
88
|
+
this.cache.clear()
|
|
89
|
+
this.memoryUsage = 0
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
updateOptions(
|
|
93
|
+
newOptions: Partial<CacheManagerOptions>,
|
|
94
|
+
protectedNodes?: Set<string>,
|
|
95
|
+
): void {
|
|
96
|
+
Object.assign(this.options, newOptions)
|
|
97
|
+
this.ensureCacheLimits(0, protectedNodes)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
getCachedNodeIds(): string[] {
|
|
101
|
+
return Array.from(this.cache.keys())
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
size(): number {
|
|
105
|
+
return this.cache.size
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
static estimateNodeSize(
|
|
109
|
+
positions: Float64Array | Float32Array,
|
|
110
|
+
colors: Float32Array,
|
|
111
|
+
): number {
|
|
112
|
+
const positionSize =
|
|
113
|
+
positions.length * (positions instanceof Float64Array ? 8 : 4)
|
|
114
|
+
const colorSize = colors.length * 4
|
|
115
|
+
return positionSize + colorSize + 1024
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
static createNodeData(
|
|
119
|
+
nodeId: string,
|
|
120
|
+
positions: Float64Array,
|
|
121
|
+
colors: Float32Array,
|
|
122
|
+
materialConfig: CachedNodeData['materialConfig'],
|
|
123
|
+
): CachedNodeData {
|
|
124
|
+
return {
|
|
125
|
+
nodeId,
|
|
126
|
+
positions: new Float64Array(positions),
|
|
127
|
+
colors: new Float32Array(colors),
|
|
128
|
+
pointCount: positions.length / 3,
|
|
129
|
+
materialConfig: { ...materialConfig },
|
|
130
|
+
lastAccessed: Date.now(),
|
|
131
|
+
sizeBytes: CacheManager.estimateNodeSize(positions, colors),
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
private ensureCacheLimits(
|
|
136
|
+
newItemSize: number,
|
|
137
|
+
protectedNodes?: Set<string>,
|
|
138
|
+
): void {
|
|
139
|
+
while (
|
|
140
|
+
this.cache.size >= this.options.maxNodes ||
|
|
141
|
+
this.memoryUsage + newItemSize > this.options.maxMemoryBytes
|
|
142
|
+
) {
|
|
143
|
+
if (this.cache.size === 0) break
|
|
144
|
+
|
|
145
|
+
// Map iterates in insertion order; first key is the LRU candidate
|
|
146
|
+
let lruNodeId: string | null = null
|
|
147
|
+
for (const nodeId of this.cache.keys()) {
|
|
148
|
+
if (!protectedNodes?.has(nodeId)) {
|
|
149
|
+
lruNodeId = nodeId
|
|
150
|
+
break
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (!lruNodeId) {
|
|
155
|
+
this.log(
|
|
156
|
+
'Warning: Cannot evict any nodes - all are protected. Cache limit exceeded.',
|
|
157
|
+
)
|
|
158
|
+
break
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
this.delete(lruNodeId)
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private disposeNodeResources(data: CachedNodeData): void {
|
|
166
|
+
data.geometry?.dispose()
|
|
167
|
+
if (data.points?.material instanceof THREE.Material) {
|
|
168
|
+
data.points.material.dispose()
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
private formatBytes(bytes: number): string {
|
|
173
|
+
const sizes = ['B', 'KB', 'MB', 'GB']
|
|
174
|
+
if (bytes === 0) return '0 B'
|
|
175
|
+
const i = Math.floor(Math.log(bytes) / Math.log(1024))
|
|
176
|
+
return `${Math.round((bytes / 1024 ** i) * 100) / 100} ${sizes[i]}`
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
private log(message: string): void {
|
|
180
|
+
if (this.options.debug) {
|
|
181
|
+
console.log('[CacheManager]', message)
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|