@ztgkzhaohao/geo-effect-kit 0.1.0
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/LICENSE +21 -0
- package/README.md +40 -0
- package/dist/index.d.ts +947 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4089 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,4089 @@
|
|
|
1
|
+
import { BoundingSphere, BillboardGraphics, CallbackPositionProperty, CallbackProperty, Cartesian2, Cartesian3, Cartesian4, CircleGeometry, Color, ColorMaterialProperty, ConstantPositionProperty, ConstantProperty, CustomDataSource, EllipsoidSurfaceAppearance, Event, GeometryInstance, GroundPrimitive, HeadingPitchRoll, HeadingPitchRange, HeightReference, HorizontalOrigin, Material, Math as CesiumMath, PointGraphics, PolylineGlowMaterialProperty, PolygonGeometry, PolygonHierarchy, PostProcessStage, Primitive, Rectangle, Transforms, VerticalOrigin, } from 'cesium';
|
|
2
|
+
import { decompressFrames, parseGIF } from 'gifuct-js';
|
|
3
|
+
export const GEO_RADAR_SCAN_MATERIAL_TYPE = 'GeoRadarScanMaterial';
|
|
4
|
+
export const GEO_RIPPLE_SPREAD_MATERIAL_TYPE = 'GeoRippleSpreadMaterial';
|
|
5
|
+
export const GEO_LIGHT_WALL_MATERIAL_TYPE = 'GeoLightWallMaterial';
|
|
6
|
+
export const GEO_SCAN_CONE_MATERIAL_TYPE = 'GeoScanConeMaterial';
|
|
7
|
+
export const GEO_SHIELD_DOME_MATERIAL_TYPE = 'GeoShieldDomeMaterial';
|
|
8
|
+
export const GEO_TEMPERATURE_FIELD_MATERIAL_TYPE = 'GeoTemperatureFieldMaterial';
|
|
9
|
+
export const GEO_WATER_SURFACE_MATERIAL_TYPE = 'GeoWaterSurfaceMaterial';
|
|
10
|
+
export const RADAR_SCAN_TYPE_VALUES = ['classic', 'sector', 'pulse', 'grid'];
|
|
11
|
+
export const RIPPLE_SPREAD_TYPE_VALUES = ['water', 'energy', 'soft'];
|
|
12
|
+
export const SCENE_WEATHER_TYPE_VALUES = ['rain', 'snow', 'fog', 'lightning'];
|
|
13
|
+
export const POST_PROCESS_TYPE_VALUES = ['bloom', 'night-vision', 'black-white', 'brightness', 'mosaic', 'depth-of-field'];
|
|
14
|
+
export const POLYLINE_FLOW_TYPE_VALUES = ['dispatch', 'migration', 'attack', 'comet', 'electric'];
|
|
15
|
+
export const FLY_LINE_MODE_VALUES = ['single-arc', 'hub-spoke', 'bidirectional'];
|
|
16
|
+
export const LIGHT_WALL_TYPE_VALUES = ['security', 'warning', 'data', 'fence', 'pulse'];
|
|
17
|
+
export const SCAN_CONE_TYPE_VALUES = ['searchlight', 'radar', 'camera', 'drone', 'alarm'];
|
|
18
|
+
export const SHIELD_DOME_TYPE_VALUES = ['hex', 'plasma', 'matrix', 'aegis', 'storm'];
|
|
19
|
+
export const WATER_SURFACE_TYPE_VALUES = ['river', 'lake', 'flood'];
|
|
20
|
+
export function createRadarScanEffect(viewer, options) {
|
|
21
|
+
return new RadarScanEffect(viewer, options);
|
|
22
|
+
}
|
|
23
|
+
export function createRippleSpreadEffect(viewer, options) {
|
|
24
|
+
return new RippleSpreadEffect(viewer, options);
|
|
25
|
+
}
|
|
26
|
+
export function createSceneWeatherEffect(viewer, options = {}) {
|
|
27
|
+
return new SceneWeatherEffect(viewer, options);
|
|
28
|
+
}
|
|
29
|
+
export function createPostProcessEffect(viewer, options = {}) {
|
|
30
|
+
return new PostProcessEffect(viewer, options);
|
|
31
|
+
}
|
|
32
|
+
export function createPolylineFlowEffect(viewer, options) {
|
|
33
|
+
return new PolylineFlowEffect(viewer, options);
|
|
34
|
+
}
|
|
35
|
+
export function createFlyLineEffect(viewer, options) {
|
|
36
|
+
return new FlyLineEffect(viewer, options);
|
|
37
|
+
}
|
|
38
|
+
export function createPipeFlowEffect(viewer, options) {
|
|
39
|
+
return new PipeFlowEffect(viewer, options);
|
|
40
|
+
}
|
|
41
|
+
export function createLightWallEffect(viewer, options) {
|
|
42
|
+
return new LightWallEffect(viewer, options);
|
|
43
|
+
}
|
|
44
|
+
export function createScanConeEffect(viewer, options) {
|
|
45
|
+
return new ScanConeEffect(viewer, options);
|
|
46
|
+
}
|
|
47
|
+
export function createShieldDomeEffect(viewer, options) {
|
|
48
|
+
return new ShieldDomeEffect(viewer, options);
|
|
49
|
+
}
|
|
50
|
+
export function createTemperatureFieldEffect(viewer, options) {
|
|
51
|
+
return new TemperatureFieldEffect(viewer, options);
|
|
52
|
+
}
|
|
53
|
+
export function createFireBillboardEffect(viewer, options) {
|
|
54
|
+
return new FireBillboardEffect(viewer, options);
|
|
55
|
+
}
|
|
56
|
+
export function createWaterSurfaceEffect(viewer, options) {
|
|
57
|
+
return new WaterSurfaceEffect(viewer, options);
|
|
58
|
+
}
|
|
59
|
+
export function normalizeRadarScanOptions(options) {
|
|
60
|
+
return {
|
|
61
|
+
center: {
|
|
62
|
+
longitude: finiteOr(options.center.longitude, 0),
|
|
63
|
+
latitude: finiteOr(options.center.latitude, 0),
|
|
64
|
+
},
|
|
65
|
+
radiusMeters: Math.max(1, finiteOr(options.radiusMeters, 1)),
|
|
66
|
+
type: normalizeRadarScanType(options.type),
|
|
67
|
+
color: options.color ?? '#36d6ff',
|
|
68
|
+
scanDurationMs: Math.max(1, finiteOr(options.scanDurationMs ?? 3600, 3600)),
|
|
69
|
+
opacity: clamp01(finiteOr(options.opacity ?? 0.85, 0.85)),
|
|
70
|
+
rings: options.rings ?? true,
|
|
71
|
+
showCenter: options.showCenter ?? false,
|
|
72
|
+
visible: options.visible ?? true,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
export function normalizeRippleSpreadOptions(options) {
|
|
76
|
+
return {
|
|
77
|
+
center: {
|
|
78
|
+
longitude: finiteOr(options.center.longitude, 0),
|
|
79
|
+
latitude: finiteOr(options.center.latitude, 0),
|
|
80
|
+
},
|
|
81
|
+
radiusMeters: Math.max(1, finiteOr(options.radiusMeters, 1)),
|
|
82
|
+
type: normalizeRippleSpreadType(options.type),
|
|
83
|
+
color: options.color ?? '#62e8ff',
|
|
84
|
+
ringCount: clampInteger(finiteOr(options.ringCount ?? 4, 4), 1, 12),
|
|
85
|
+
durationMs: Math.max(1, finiteOr(options.durationMs ?? 3200, 3200)),
|
|
86
|
+
opacity: clamp01(finiteOr(options.opacity ?? 0.82, 0.82)),
|
|
87
|
+
showCenter: options.showCenter ?? false,
|
|
88
|
+
visible: options.visible ?? true,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
export function normalizeSceneWeatherOptions(options = {}) {
|
|
92
|
+
const color = options.color && options.color.trim().length > 0 ? options.color : '#d8f3ff';
|
|
93
|
+
return {
|
|
94
|
+
type: normalizeSceneWeatherType(options.type),
|
|
95
|
+
intensity: clamp01(finiteOr(options.intensity ?? 0.55, 0.55)),
|
|
96
|
+
speed: clamp(finiteOr(options.speed ?? 1, 1), 0.05, 8),
|
|
97
|
+
windDirection: finiteOr(options.windDirection ?? 115, 115),
|
|
98
|
+
color,
|
|
99
|
+
visible: options.visible ?? true,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
export function normalizePostProcessOptions(options = {}) {
|
|
103
|
+
return {
|
|
104
|
+
type: normalizePostProcessType(options.type),
|
|
105
|
+
strength: clamp01(finiteOr(options.strength ?? 0.65, 0.65)),
|
|
106
|
+
brightness: clamp(finiteOr(options.brightness ?? 1, 1), 0, 3),
|
|
107
|
+
contrast: clamp(finiteOr(options.contrast ?? 1, 1), 0, 3),
|
|
108
|
+
saturation: clamp(finiteOr(options.saturation ?? 1, 1), 0, 3),
|
|
109
|
+
visible: options.visible ?? true,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
export function normalizePolylineFlowOptions(options) {
|
|
113
|
+
return {
|
|
114
|
+
positions: normalizePositions(options.positions, false),
|
|
115
|
+
type: normalizePolylineFlowType(options.type),
|
|
116
|
+
color: options.color ?? '#33f7ff',
|
|
117
|
+
speed: clamp(finiteOr(options.speed ?? 1, 1), 0.05, 8),
|
|
118
|
+
width: Math.max(1, finiteOr(options.width ?? 6, 6)),
|
|
119
|
+
trailLength: clamp(finiteOr(options.trailLength ?? 0.32, 0.32), 0.02, 0.95),
|
|
120
|
+
pulseCount: clampInteger(finiteOr(options.pulseCount ?? 3, 3), 1, 12),
|
|
121
|
+
cornerRadius: clamp(finiteOr(options.cornerRadius ?? 0, 0), 0, 0.45),
|
|
122
|
+
glowPower: clamp(finiteOr(options.glowPower ?? 0.22, 0.22), 0, 1),
|
|
123
|
+
taperPower: clamp(finiteOr(options.taperPower ?? 0.72, 0.72), 0, 1),
|
|
124
|
+
clampToGround: options.clampToGround ?? true,
|
|
125
|
+
visible: options.visible ?? true,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
export function normalizeFlyLineOptions(options) {
|
|
129
|
+
return {
|
|
130
|
+
lines: normalizeFlyLineRoutes(options.lines),
|
|
131
|
+
mode: normalizeFlyLineMode(options.mode),
|
|
132
|
+
color: options.color ?? '#5ee8ff',
|
|
133
|
+
speed: clamp(finiteOr(options.speed ?? 1, 1), 0.05, 8),
|
|
134
|
+
width: Math.max(1, finiteOr(options.width ?? 4, 4)),
|
|
135
|
+
arcHeight: Math.max(0, finiteOr(options.arcHeight ?? 38000, 38000)),
|
|
136
|
+
trailLength: clamp(finiteOr(options.trailLength ?? 0.28, 0.28), 0.02, 0.95),
|
|
137
|
+
pulseCount: clampInteger(finiteOr(options.pulseCount ?? 3, 3), 1, 12),
|
|
138
|
+
glowPower: clamp(finiteOr(options.glowPower ?? 0.26, 0.26), 0, 1),
|
|
139
|
+
taperPower: clamp(finiteOr(options.taperPower ?? 0.62, 0.62), 0, 1),
|
|
140
|
+
showEndpoints: options.showEndpoints ?? true,
|
|
141
|
+
visible: options.visible ?? true,
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
export function normalizePipeFlowOptions(options) {
|
|
145
|
+
return {
|
|
146
|
+
positions: normalizePositions(options.positions, false),
|
|
147
|
+
color: options.color ?? '#45dfff',
|
|
148
|
+
speed: clamp(finiteOr(options.speed ?? 1, 1), 0.05, 8),
|
|
149
|
+
width: Math.max(1, finiteOr(options.width ?? 12, 12)),
|
|
150
|
+
pipeOpacity: clamp01(finiteOr(options.pipeOpacity ?? 0.32, 0.32)),
|
|
151
|
+
waterOpacity: clamp01(finiteOr(options.waterOpacity ?? 0.86, 0.86)),
|
|
152
|
+
cornerRadius: clamp(finiteOr(options.cornerRadius ?? 0.18, 0.18), 0, 0.45),
|
|
153
|
+
bubbleDensity: clampInteger(finiteOr(options.bubbleDensity ?? 6, 6), 0, 16),
|
|
154
|
+
clampToGround: options.clampToGround ?? true,
|
|
155
|
+
visible: options.visible ?? true,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
export function normalizeLightWallOptions(options) {
|
|
159
|
+
return {
|
|
160
|
+
positions: normalizePositions(options.positions, true),
|
|
161
|
+
type: normalizeLightWallType(options.type),
|
|
162
|
+
color: options.color ?? '#27f5ff',
|
|
163
|
+
height: Math.max(1, finiteOr(options.height ?? 3200, 3200)),
|
|
164
|
+
speed: clamp(finiteOr(options.speed ?? 1, 1), 0.05, 8),
|
|
165
|
+
opacity: clamp01(finiteOr(options.opacity ?? 0.72, 0.72)),
|
|
166
|
+
scanLineCount: clampInteger(finiteOr(options.scanLineCount ?? 4, 4), 1, 16),
|
|
167
|
+
breathing: options.breathing ?? true,
|
|
168
|
+
outline: options.outline ?? true,
|
|
169
|
+
visible: options.visible ?? true,
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
export function normalizeScanConeOptions(options) {
|
|
173
|
+
return {
|
|
174
|
+
center: normalizePosition(options.center),
|
|
175
|
+
type: normalizeScanConeType(options.type),
|
|
176
|
+
color: options.color ?? '#7cf7ff',
|
|
177
|
+
radiusMeters: Math.max(1, finiteOr(options.radiusMeters ?? 1800, 1800)),
|
|
178
|
+
lengthMeters: Math.max(1, finiteOr(options.lengthMeters ?? 4800, 4800)),
|
|
179
|
+
speed: clamp(finiteOr(options.speed ?? 1, 1), 0.05, 8),
|
|
180
|
+
opacity: clamp01(finiteOr(options.opacity ?? 0.62, 0.62)),
|
|
181
|
+
aperture: clamp(finiteOr(options.aperture ?? 34, 34), 8, 120),
|
|
182
|
+
heading: finiteOr(options.heading ?? 0, 0),
|
|
183
|
+
pitch: finiteOr(options.pitch ?? 0, 0),
|
|
184
|
+
showOrigin: options.showOrigin ?? true,
|
|
185
|
+
visible: options.visible ?? true,
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
export function normalizeShieldDomeOptions(options) {
|
|
189
|
+
return {
|
|
190
|
+
center: normalizePosition(options.center),
|
|
191
|
+
radiusMeters: Math.max(1, finiteOr(options.radiusMeters, 1)),
|
|
192
|
+
type: normalizeShieldDomeType(options.type),
|
|
193
|
+
color: options.color ?? '#57f7ff',
|
|
194
|
+
speed: clamp(finiteOr(options.speed ?? 1, 1), 0.05, 8),
|
|
195
|
+
opacity: clamp01(finiteOr(options.opacity ?? 0.56, 0.56)),
|
|
196
|
+
gridDensity: clampInteger(finiteOr(options.gridDensity ?? 14, 14), 2, 40),
|
|
197
|
+
pulseStrength: clamp01(finiteOr(options.pulseStrength ?? 0.72, 0.72)),
|
|
198
|
+
ring: options.ring ?? true,
|
|
199
|
+
visible: options.visible ?? true,
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
export function normalizeTemperatureFieldOptions(options) {
|
|
203
|
+
const polygons = normalizeTemperatureFieldPolygons(options.polygons);
|
|
204
|
+
const bounds = normalizeTemperatureFieldBounds(options.bounds) ?? getTemperatureFieldBounds(polygons);
|
|
205
|
+
return {
|
|
206
|
+
polygons,
|
|
207
|
+
stops: normalizeTemperatureFieldStops(options.stops),
|
|
208
|
+
samples: normalizeTemperatureFieldSamples(options.samples),
|
|
209
|
+
seed: normalizeSeed(options.seed),
|
|
210
|
+
opacity: clamp01(finiteOr(options.opacity ?? 0.76, 0.76)),
|
|
211
|
+
bounds,
|
|
212
|
+
noiseStrength: clamp01(finiteOr(options.noiseStrength ?? 0.42, 0.42)),
|
|
213
|
+
contourLines: options.contourLines ?? true,
|
|
214
|
+
contourStrength: clamp01(finiteOr(options.contourStrength ?? 0.18, 0.18)),
|
|
215
|
+
outline: options.outline ?? true,
|
|
216
|
+
outlineColor: options.outlineColor ?? '#dff8ff',
|
|
217
|
+
outlineWidth: Math.max(1, finiteOr(options.outlineWidth ?? 5, 5)),
|
|
218
|
+
visible: options.visible ?? true,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
export function normalizeFireBillboardOptions(options) {
|
|
222
|
+
return {
|
|
223
|
+
points: normalizeFireBillboardPoints(options.points),
|
|
224
|
+
scale: clamp(finiteOr(options.scale ?? 1, 1), 0.1, 8),
|
|
225
|
+
frameIntervalMs: clamp(finiteOr(options.frameIntervalMs ?? 80, 80), 16, 2000),
|
|
226
|
+
clampToGround: options.clampToGround ?? true,
|
|
227
|
+
disableDepthTestDistance: Math.max(0, finiteOr(options.disableDepthTestDistance ?? Number.POSITIVE_INFINITY, Number.POSITIVE_INFINITY)),
|
|
228
|
+
visible: options.visible ?? true,
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
export function normalizeWaterSurfaceOptions(options) {
|
|
232
|
+
const color = options.color && options.color.trim().length > 0 ? options.color : '#3de7ff';
|
|
233
|
+
return {
|
|
234
|
+
polygon: normalizePositions(options.polygon, true),
|
|
235
|
+
type: normalizeWaterSurfaceType(options.type),
|
|
236
|
+
color,
|
|
237
|
+
height: Math.max(0, finiteOr(options.height ?? 0, 0)),
|
|
238
|
+
speed: clamp(finiteOr(options.speed ?? 1, 1), 0.05, 8),
|
|
239
|
+
opacity: clamp01(finiteOr(options.opacity ?? 0.72, 0.72)),
|
|
240
|
+
waveStrength: clamp01(finiteOr(options.waveStrength ?? 0.48, 0.48)),
|
|
241
|
+
reflectionStrength: clamp01(finiteOr(options.reflectionStrength ?? 0.36, 0.36)),
|
|
242
|
+
distortionScale: clamp(finiteOr(options.distortionScale ?? 18, 18), 0, 64),
|
|
243
|
+
reflectivity: clamp01(finiteOr(options.reflectivity ?? 0.58, 0.58)),
|
|
244
|
+
refractionStrength: clamp01(finiteOr(options.refractionStrength ?? 0.42, 0.42)),
|
|
245
|
+
fresnelPower: clamp(finiteOr(options.fresnelPower ?? 4, 4), 1, 12),
|
|
246
|
+
flowDirection: finiteOr(options.flowDirection ?? 90, 90),
|
|
247
|
+
outline: options.outline ?? true,
|
|
248
|
+
visible: options.visible ?? true,
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
export function shouldRebuildRadarScan(previous, next) {
|
|
252
|
+
return (previous.radiusMeters !== next.radiusMeters ||
|
|
253
|
+
previous.center.longitude !== next.center.longitude ||
|
|
254
|
+
previous.center.latitude !== next.center.latitude);
|
|
255
|
+
}
|
|
256
|
+
export function shouldRebuildRippleSpread(previous, next) {
|
|
257
|
+
return (previous.radiusMeters !== next.radiusMeters ||
|
|
258
|
+
previous.center.longitude !== next.center.longitude ||
|
|
259
|
+
previous.center.latitude !== next.center.latitude);
|
|
260
|
+
}
|
|
261
|
+
export function shouldRebuildPolylineFlow(previous, next) {
|
|
262
|
+
return (previous.clampToGround !== next.clampToGround ||
|
|
263
|
+
previous.cornerRadius !== next.cornerRadius ||
|
|
264
|
+
previous.positions.length !== next.positions.length ||
|
|
265
|
+
!positionsEqual(previous.positions, next.positions));
|
|
266
|
+
}
|
|
267
|
+
export function shouldRebuildFlyLine(previous, next) {
|
|
268
|
+
return (previous.mode !== next.mode ||
|
|
269
|
+
previous.arcHeight !== next.arcHeight ||
|
|
270
|
+
previous.lines.length !== next.lines.length ||
|
|
271
|
+
!flyLineRoutesEqual(previous.lines, next.lines));
|
|
272
|
+
}
|
|
273
|
+
export function shouldRebuildPipeFlow(previous, next) {
|
|
274
|
+
return (previous.clampToGround !== next.clampToGround ||
|
|
275
|
+
previous.cornerRadius !== next.cornerRadius ||
|
|
276
|
+
previous.bubbleDensity !== next.bubbleDensity ||
|
|
277
|
+
previous.positions.length !== next.positions.length ||
|
|
278
|
+
!positionsEqual(previous.positions, next.positions));
|
|
279
|
+
}
|
|
280
|
+
export function shouldRebuildLightWall(previous, next) {
|
|
281
|
+
return (previous.height !== next.height ||
|
|
282
|
+
previous.positions.length !== next.positions.length ||
|
|
283
|
+
!positionsEqual(previous.positions, next.positions));
|
|
284
|
+
}
|
|
285
|
+
export function shouldRebuildScanCone(previous, next) {
|
|
286
|
+
return (previous.radiusMeters !== next.radiusMeters ||
|
|
287
|
+
previous.lengthMeters !== next.lengthMeters ||
|
|
288
|
+
previous.aperture !== next.aperture ||
|
|
289
|
+
!positionsEqual([previous.center], [next.center]));
|
|
290
|
+
}
|
|
291
|
+
export function shouldRebuildShieldDome(previous, next) {
|
|
292
|
+
return previous.radiusMeters !== next.radiusMeters || !positionsEqual([previous.center], [next.center]);
|
|
293
|
+
}
|
|
294
|
+
export function shouldRebuildTemperatureField(previous, next) {
|
|
295
|
+
return !temperatureFieldPolygonsEqual(previous.polygons, next.polygons);
|
|
296
|
+
}
|
|
297
|
+
export function shouldRebuildFireBillboard(previous, next) {
|
|
298
|
+
return (previous.clampToGround !== next.clampToGround ||
|
|
299
|
+
previous.points.length !== next.points.length ||
|
|
300
|
+
!fireBillboardPointsEqual(previous.points, next.points));
|
|
301
|
+
}
|
|
302
|
+
export function shouldRebuildWaterSurface(previous, next) {
|
|
303
|
+
return (previous.height !== next.height ||
|
|
304
|
+
previous.outline !== next.outline ||
|
|
305
|
+
previous.polygon.length !== next.polygon.length ||
|
|
306
|
+
!positionsEqual(previous.polygon, next.polygon));
|
|
307
|
+
}
|
|
308
|
+
export function buildPolylineFlowSegmentWeights(segmentCount, type = 'dispatch', progress = 0) {
|
|
309
|
+
const count = clampInteger(finiteOr(segmentCount, 0), 0, 128);
|
|
310
|
+
if (count <= 0)
|
|
311
|
+
return [];
|
|
312
|
+
const normalizedType = normalizePolylineFlowType(type);
|
|
313
|
+
const head = fract(finiteOr(progress, 0)) * Math.max(1, count - 1);
|
|
314
|
+
const profile = getPolylineFlowProfile(normalizedType);
|
|
315
|
+
return Array.from({ length: count }, (_, index) => {
|
|
316
|
+
const distance = Math.abs(index - head);
|
|
317
|
+
const wrappedDistance = Math.min(distance, count - distance);
|
|
318
|
+
const weight = 1 - wrappedDistance / Math.max(1, profile.tail);
|
|
319
|
+
return roundWeight(clamp01(weight) ** profile.power);
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
export function buildRadarScanMaterialSource(_options) {
|
|
323
|
+
return `
|
|
324
|
+
// ${GEO_RADAR_SCAN_MATERIAL_TYPE}
|
|
325
|
+
czm_material czm_getMaterial(czm_materialInput materialInput)
|
|
326
|
+
{
|
|
327
|
+
czm_material material = czm_getDefaultMaterial(materialInput);
|
|
328
|
+
vec2 st = materialInput.st;
|
|
329
|
+
vec2 radarCenter = vec2(0.5);
|
|
330
|
+
vec2 vectorToPixel = st - radarCenter;
|
|
331
|
+
float d = distance(st, radarCenter);
|
|
332
|
+
float inside = smoothstep(0.5, 0.495, d);
|
|
333
|
+
float outerRing = ringsEnabled * smoothstep(0.018, 0.0, abs(d - 0.49));
|
|
334
|
+
float middleRing = ringsEnabled * smoothstep(0.008, 0.0, abs(d - 0.33)) * 0.16;
|
|
335
|
+
float innerRing = ringsEnabled * smoothstep(0.008, 0.0, abs(d - 0.18)) * 0.18;
|
|
336
|
+
float scanAngle = fract((czm_frameNumber * 16.6667) / scanDurationMs) * 6.28318530718;
|
|
337
|
+
vec2 scanDirection = vec2(cos(scanAngle), sin(scanAngle));
|
|
338
|
+
vec2 pixelDirection = normalize(vectorToPixel + vec2(0.00001));
|
|
339
|
+
float directionMatch = dot(pixelDirection, scanDirection);
|
|
340
|
+
float radialFade = smoothstep(0.06, 0.18, d) * (1.0 - smoothstep(0.35, 0.5, d));
|
|
341
|
+
float classicEnabled = 1.0 - step(1.5, radarType);
|
|
342
|
+
float sectorEnabled = step(1.5, radarType) * (1.0 - step(2.5, radarType));
|
|
343
|
+
float pulseEnabled = step(2.5, radarType) * (1.0 - step(3.5, radarType));
|
|
344
|
+
float gridEnabled = step(3.5, radarType);
|
|
345
|
+
float classicScan = smoothstep(0.36, 0.98, directionMatch) * radialFade * inside;
|
|
346
|
+
float classicLine = smoothstep(0.955, 1.0, directionMatch) * radialFade * inside;
|
|
347
|
+
float sectorScan = smoothstep(0.06, 0.92, directionMatch) * radialFade * inside;
|
|
348
|
+
float sectorEdge = (smoothstep(0.84, 1.0, directionMatch) + smoothstep(0.08, 0.0, abs(directionMatch - 0.12))) * radialFade * inside;
|
|
349
|
+
float pulseScan = pow(max(directionMatch, 0.0), 16.0) * radialFade * inside;
|
|
350
|
+
float pulseHead = smoothstep(0.975, 1.0, directionMatch) * radialFade * inside;
|
|
351
|
+
float pulseWake = smoothstep(0.28, 0.95, directionMatch) * radialFade * inside * (1.0 - smoothstep(0.42, 0.5, d));
|
|
352
|
+
float gridLineX = smoothstep(0.0035, 0.0, abs(fract(st.x * 12.0) - 0.5));
|
|
353
|
+
float gridLineY = smoothstep(0.0035, 0.0, abs(fract(st.y * 12.0) - 0.5));
|
|
354
|
+
float gridMask = (gridLineX + gridLineY) * radialFade * inside * smoothstep(0.28, 0.98, directionMatch);
|
|
355
|
+
float gridScan = max(gridMask * 0.42, classicLine * 0.85);
|
|
356
|
+
float scanTail = classicScan * classicEnabled + sectorScan * sectorEnabled + pulseWake * pulseEnabled + gridScan * gridEnabled;
|
|
357
|
+
float sweepLine = classicLine * classicEnabled + sectorEdge * sectorEnabled + pulseHead * pulseEnabled + classicLine * gridEnabled;
|
|
358
|
+
float centerGlow = smoothstep(0.065, 0.0, d) * (1.0 + pulseEnabled * 0.45 + gridEnabled * 0.18);
|
|
359
|
+
float crossLine = (smoothstep(0.003, 0.0, abs(st.x - 0.5)) + smoothstep(0.003, 0.0, abs(st.y - 0.5))) * (0.1 + gridEnabled * 0.2);
|
|
360
|
+
vec3 sectorTint = mix(color.rgb, vec3(0.7, 1.0, 0.32), 0.26);
|
|
361
|
+
vec3 pulseTint = mix(color.rgb, vec3(1.0, 0.26, 0.88), 0.36);
|
|
362
|
+
vec3 gridTint = mix(color.rgb, vec3(0.42, 0.72, 1.0), 0.28);
|
|
363
|
+
vec3 styleColor = color.rgb * classicEnabled + sectorTint * sectorEnabled + pulseTint * pulseEnabled + gridTint * gridEnabled;
|
|
364
|
+
float bodyGlow = sectorEnabled * sectorScan * 0.1 + pulseEnabled * pulseScan * 0.18 + gridEnabled * gridMask * 0.08;
|
|
365
|
+
material.diffuse = styleColor * (0.34 + scanTail * 0.42 + outerRing * 0.32 + bodyGlow);
|
|
366
|
+
material.emission = styleColor * (scanTail * 0.55 + sweepLine * (0.72 + pulseEnabled * 0.45) + outerRing * 0.8 + centerGlow * 0.45 + bodyGlow * 1.6);
|
|
367
|
+
material.alpha = opacity * (inside * 0.08 + scanTail * 0.28 + sweepLine * (0.18 + sectorEnabled * 0.14 + pulseEnabled * 0.2) + outerRing * 0.56 + middleRing + innerRing + centerGlow * 0.18 + crossLine + bodyGlow);
|
|
368
|
+
return material;
|
|
369
|
+
}
|
|
370
|
+
`;
|
|
371
|
+
}
|
|
372
|
+
export function buildRippleSpreadMaterialSource() {
|
|
373
|
+
return `
|
|
374
|
+
// ${GEO_RIPPLE_SPREAD_MATERIAL_TYPE}
|
|
375
|
+
czm_material czm_getMaterial(czm_materialInput materialInput)
|
|
376
|
+
{
|
|
377
|
+
czm_material material = czm_getDefaultMaterial(materialInput);
|
|
378
|
+
vec2 st = materialInput.st;
|
|
379
|
+
vec2 center = vec2(0.5);
|
|
380
|
+
float d = distance(st, center);
|
|
381
|
+
float inside = smoothstep(0.5, 0.495, d);
|
|
382
|
+
float time = fract((czm_frameNumber * 16.6667) / durationMs);
|
|
383
|
+
float safeRingCount = max(ringCount, 1.0);
|
|
384
|
+
float waterRipple = 0.0;
|
|
385
|
+
float energyRipple = 0.0;
|
|
386
|
+
float softRipple = 0.0;
|
|
387
|
+
|
|
388
|
+
for (int i = 0; i < 12; i++) {
|
|
389
|
+
float index = float(i);
|
|
390
|
+
float enabled = step(index + 0.5, safeRingCount);
|
|
391
|
+
float progress = fract(time - index / safeRingCount + 1.0);
|
|
392
|
+
float rippleRadius = progress * 0.49;
|
|
393
|
+
float edgeFade = smoothstep(0.025, 0.13, rippleRadius) * (1.0 - smoothstep(0.39, 0.5, rippleRadius));
|
|
394
|
+
float waterLine = smoothstep(0.026, 0.0, abs(d - rippleRadius)) * edgeFade;
|
|
395
|
+
float energyLine = smoothstep(0.011, 0.0, abs(d - rippleRadius)) * edgeFade;
|
|
396
|
+
float softLine = smoothstep(0.045, 0.0, abs(d - rippleRadius)) * edgeFade;
|
|
397
|
+
waterRipple += enabled * waterLine * (1.0 - progress * 0.34);
|
|
398
|
+
energyRipple += enabled * energyLine * (1.24 - progress * 0.18);
|
|
399
|
+
softRipple += enabled * softLine * (0.78 - progress * 0.22);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
float energyEnabled = step(1.5, rippleType) * (1.0 - step(2.5, rippleType));
|
|
403
|
+
float softEnabled = step(2.5, rippleType);
|
|
404
|
+
float waterEnabled = 1.0 - energyEnabled - softEnabled;
|
|
405
|
+
float rippleStrength = waterRipple * waterEnabled + energyRipple * energyEnabled + softRipple * softEnabled;
|
|
406
|
+
float centerGlow = smoothstep(0.07, 0.0, d) * (0.42 + energyEnabled * 0.58);
|
|
407
|
+
float outerGlow = smoothstep(0.5, 0.26, d) * inside * (0.04 + softEnabled * 0.05);
|
|
408
|
+
float shimmer = sin((d * 82.0) - (time * 6.28318530718)) * 0.018 * waterEnabled * inside;
|
|
409
|
+
vec3 energyTint = mix(color.rgb, vec3(1.0, 0.24, 0.86), 0.42);
|
|
410
|
+
vec3 softTint = mix(color.rgb, vec3(0.72, 0.94, 1.0), 0.28);
|
|
411
|
+
vec3 styleColor = color.rgb * waterEnabled + energyTint * energyEnabled + softTint * softEnabled;
|
|
412
|
+
material.diffuse = styleColor * (0.24 + rippleStrength * 0.3 + outerGlow);
|
|
413
|
+
material.emission = styleColor * (rippleStrength * (0.56 + energyEnabled * 0.42) + centerGlow * 0.48);
|
|
414
|
+
material.alpha = opacity * (inside * (0.035 + softEnabled * 0.04) + shimmer + rippleStrength * (0.35 + energyEnabled * 0.24) + centerGlow * 0.18 + outerGlow);
|
|
415
|
+
return material;
|
|
416
|
+
}
|
|
417
|
+
`;
|
|
418
|
+
}
|
|
419
|
+
export function buildSceneWeatherPostProcessSource() {
|
|
420
|
+
return `
|
|
421
|
+
uniform sampler2D colorTexture;
|
|
422
|
+
uniform float weatherType;
|
|
423
|
+
uniform float intensity;
|
|
424
|
+
uniform float speed;
|
|
425
|
+
uniform float windDirection;
|
|
426
|
+
uniform vec4 color;
|
|
427
|
+
in vec2 v_textureCoordinates;
|
|
428
|
+
|
|
429
|
+
float geoWeatherHash(vec2 p)
|
|
430
|
+
{
|
|
431
|
+
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
float geoWeatherNoise(vec2 p)
|
|
435
|
+
{
|
|
436
|
+
vec2 i = floor(p);
|
|
437
|
+
vec2 f = fract(p);
|
|
438
|
+
float a = geoWeatherHash(i);
|
|
439
|
+
float b = geoWeatherHash(i + vec2(1.0, 0.0));
|
|
440
|
+
float c = geoWeatherHash(i + vec2(0.0, 1.0));
|
|
441
|
+
float d = geoWeatherHash(i + vec2(1.0, 1.0));
|
|
442
|
+
vec2 u = f * f * (3.0 - 2.0 * f);
|
|
443
|
+
return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
void main()
|
|
447
|
+
{
|
|
448
|
+
vec4 sceneColor = texture(colorTexture, v_textureCoordinates);
|
|
449
|
+
vec2 st = v_textureCoordinates;
|
|
450
|
+
float time = czm_frameNumber * 0.016667 * speed;
|
|
451
|
+
float angle = radians(windDirection);
|
|
452
|
+
vec2 wind = vec2(cos(angle), sin(angle));
|
|
453
|
+
float rainEnabled = 1.0 - step(1.5, weatherType);
|
|
454
|
+
float snowEnabled = step(1.5, weatherType) * (1.0 - step(2.5, weatherType));
|
|
455
|
+
float fogEnabled = step(2.5, weatherType) * (1.0 - step(3.5, weatherType));
|
|
456
|
+
float lightningEnabled = step(3.5, weatherType);
|
|
457
|
+
|
|
458
|
+
vec2 rainUv = st * vec2(36.0, 1.8) + wind * time * 1.65;
|
|
459
|
+
float rainLine = smoothstep(0.965, 1.0, fract(rainUv.x + rainUv.y * 0.24));
|
|
460
|
+
rainLine *= smoothstep(0.14, 1.0, geoWeatherHash(vec2(floor(rainUv.x), floor(rainUv.y * 8.0))));
|
|
461
|
+
|
|
462
|
+
vec2 snowUv = st * 18.0 + vec2(wind.x * 0.28, -1.0) * time * 0.32;
|
|
463
|
+
float snowCell = geoWeatherNoise(snowUv);
|
|
464
|
+
float snowFlake = smoothstep(0.82, 1.0, snowCell) * (0.55 + 0.45 * geoWeatherNoise(snowUv * 0.47));
|
|
465
|
+
|
|
466
|
+
float fogNoise = geoWeatherNoise(st * 4.0 + vec2(time * 0.04, time * 0.02));
|
|
467
|
+
float fogGradient = smoothstep(0.02, 0.98, st.y);
|
|
468
|
+
float fog = (0.45 + fogNoise * 0.55) * fogGradient;
|
|
469
|
+
|
|
470
|
+
float flash = pow(max(sin(time * 2.7), 0.0), 18.0) * geoWeatherNoise(vec2(floor(time * 0.7), 7.3));
|
|
471
|
+
float bolt = smoothstep(0.018, 0.0, abs(st.x - (0.28 + geoWeatherNoise(vec2(floor(time), 2.0)) * 0.46 + sin(st.y * 34.0) * 0.025)));
|
|
472
|
+
bolt *= smoothstep(0.95, 0.35, st.y) * flash;
|
|
473
|
+
|
|
474
|
+
vec3 weatherColor = color.rgb;
|
|
475
|
+
vec3 rainColor = mix(sceneColor.rgb, weatherColor, rainLine * intensity * 0.48);
|
|
476
|
+
vec3 snowColor = mix(sceneColor.rgb, weatherColor, snowFlake * intensity * 0.58);
|
|
477
|
+
vec3 fogColor = mix(sceneColor.rgb, weatherColor, fog * intensity * 0.72);
|
|
478
|
+
vec3 lightningColor = sceneColor.rgb + weatherColor * (bolt * 1.8 + flash * 0.18) * intensity;
|
|
479
|
+
vec3 mixed = sceneColor.rgb;
|
|
480
|
+
mixed = mix(mixed, rainColor, rainEnabled);
|
|
481
|
+
mixed = mix(mixed, snowColor, snowEnabled);
|
|
482
|
+
mixed = mix(mixed, fogColor, fogEnabled);
|
|
483
|
+
mixed = mix(mixed, lightningColor, lightningEnabled);
|
|
484
|
+
out_FragColor = vec4(mixed, sceneColor.a);
|
|
485
|
+
}
|
|
486
|
+
`;
|
|
487
|
+
}
|
|
488
|
+
export function buildPostProcessSource() {
|
|
489
|
+
return `
|
|
490
|
+
uniform sampler2D colorTexture;
|
|
491
|
+
uniform float effectType;
|
|
492
|
+
uniform float strength;
|
|
493
|
+
uniform float brightness;
|
|
494
|
+
uniform float contrast;
|
|
495
|
+
uniform float saturation;
|
|
496
|
+
in vec2 v_textureCoordinates;
|
|
497
|
+
|
|
498
|
+
void main()
|
|
499
|
+
{
|
|
500
|
+
vec4 sceneColor = texture(colorTexture, v_textureCoordinates);
|
|
501
|
+
vec2 st = v_textureCoordinates;
|
|
502
|
+
vec3 colorValue = sceneColor.rgb;
|
|
503
|
+
float bloomEnabled = 1.0 - step(1.5, effectType);
|
|
504
|
+
float nightEnabled = step(1.5, effectType) * (1.0 - step(2.5, effectType));
|
|
505
|
+
float blackWhiteEnabled = step(2.5, effectType) * (1.0 - step(3.5, effectType));
|
|
506
|
+
float brightnessEnabled = step(3.5, effectType) * (1.0 - step(4.5, effectType));
|
|
507
|
+
float mosaicEnabled = step(4.5, effectType) * (1.0 - step(5.5, effectType));
|
|
508
|
+
float depthEnabled = step(5.5, effectType);
|
|
509
|
+
|
|
510
|
+
float luminance = dot(colorValue, vec3(0.299, 0.587, 0.114));
|
|
511
|
+
vec3 bloomColor = colorValue + max(colorValue - vec3(0.62), vec3(0.0)) * strength * 1.35;
|
|
512
|
+
vec3 nightColor = vec3(luminance * 0.18, luminance * 1.22, luminance * 0.34) * (0.72 + strength);
|
|
513
|
+
vec3 grayColor = vec3(luminance);
|
|
514
|
+
vec3 brightColor = (colorValue - 0.5) * contrast + 0.5;
|
|
515
|
+
brightColor = mix(vec3(dot(brightColor, vec3(0.299, 0.587, 0.114))), brightColor, saturation) * brightness;
|
|
516
|
+
|
|
517
|
+
vec2 mosaicUv = (floor(st * mix(96.0, 24.0, strength)) + 0.5) / mix(96.0, 24.0, strength);
|
|
518
|
+
vec3 mosaicColor = texture(colorTexture, mosaicUv).rgb;
|
|
519
|
+
float distanceFromFocus = distance(st, vec2(0.5));
|
|
520
|
+
vec3 depthColor = mix(colorValue, texture(colorTexture, mix(st, vec2(0.5), 0.018 + strength * 0.04)).rgb, smoothstep(0.2, 0.72, distanceFromFocus));
|
|
521
|
+
|
|
522
|
+
vec3 result = colorValue;
|
|
523
|
+
result = mix(result, bloomColor, bloomEnabled);
|
|
524
|
+
result = mix(result, nightColor, nightEnabled);
|
|
525
|
+
result = mix(result, grayColor, blackWhiteEnabled * strength);
|
|
526
|
+
result = mix(result, brightColor, brightnessEnabled);
|
|
527
|
+
result = mix(result, mosaicColor, mosaicEnabled);
|
|
528
|
+
result = mix(result, depthColor, depthEnabled);
|
|
529
|
+
out_FragColor = vec4(result, sceneColor.a);
|
|
530
|
+
}
|
|
531
|
+
`;
|
|
532
|
+
}
|
|
533
|
+
export function buildTemperatureFieldMaterialSource() {
|
|
534
|
+
return `
|
|
535
|
+
// ${GEO_TEMPERATURE_FIELD_MATERIAL_TYPE}
|
|
536
|
+
float temperatureHash(vec2 p)
|
|
537
|
+
{
|
|
538
|
+
p = fract(p * vec2(123.34, 456.21));
|
|
539
|
+
p += dot(p, p + 45.32);
|
|
540
|
+
return fract(p.x * p.y);
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
float temperatureNoise(vec2 p)
|
|
544
|
+
{
|
|
545
|
+
vec2 i = floor(p);
|
|
546
|
+
vec2 f = fract(p);
|
|
547
|
+
float a = temperatureHash(i);
|
|
548
|
+
float b = temperatureHash(i + vec2(1.0, 0.0));
|
|
549
|
+
float c = temperatureHash(i + vec2(0.0, 1.0));
|
|
550
|
+
float d = temperatureHash(i + vec2(1.0, 1.0));
|
|
551
|
+
vec2 u = f * f * (3.0 - 2.0 * f);
|
|
552
|
+
return mix(a, b, u.x) + (c - a) * u.y * (1.0 - u.x) + (d - b) * u.x * u.y;
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
vec3 temperatureRamp(float value)
|
|
556
|
+
{
|
|
557
|
+
vec3 c0 = lowColor.rgb;
|
|
558
|
+
vec3 c1 = lowerColor.rgb;
|
|
559
|
+
vec3 c2 = mediumColor.rgb;
|
|
560
|
+
vec3 c3 = higherColor.rgb;
|
|
561
|
+
vec3 c4 = highColor.rgb;
|
|
562
|
+
vec3 first = mix(c0, c1, smoothstep(0.0, 0.25, value));
|
|
563
|
+
vec3 second = mix(c1, c2, smoothstep(0.18, 0.5, value));
|
|
564
|
+
vec3 third = mix(c2, c3, smoothstep(0.42, 0.72, value));
|
|
565
|
+
vec3 fourth = mix(c3, c4, smoothstep(0.64, 1.0, value));
|
|
566
|
+
return mix(mix(first, second, smoothstep(0.18, 0.38, value)), mix(third, fourth, smoothstep(0.58, 0.82, value)), smoothstep(0.42, 0.64, value));
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
float randomBlob(vec2 st, vec2 center, float radius)
|
|
570
|
+
{
|
|
571
|
+
vec2 offset = st - center;
|
|
572
|
+
float distanceSquared = dot(offset, offset);
|
|
573
|
+
return exp(-distanceSquared / max(0.0001, radius));
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
float randomField(vec2 st)
|
|
577
|
+
{
|
|
578
|
+
vec2 randomCenter0 = vec2(0.18 + temperatureHash(seedVector.xy + vec2(0.11, 0.31)) * 0.64, 0.14 + temperatureHash(seedVector.zw + vec2(0.29, 0.07)) * 0.7);
|
|
579
|
+
vec2 randomCenter1 = vec2(0.18 + temperatureHash(seedVector.yz + vec2(0.43, 0.17)) * 0.64, 0.14 + temperatureHash(seedVector.wx + vec2(0.19, 0.53)) * 0.7);
|
|
580
|
+
vec2 randomCenter2 = vec2(0.18 + temperatureHash(seedVector.zx + vec2(0.73, 0.23)) * 0.64, 0.14 + temperatureHash(seedVector.yw + vec2(0.61, 0.37)) * 0.7);
|
|
581
|
+
vec2 randomCenter3 = vec2(0.18 + temperatureHash(seedVector.xw + vec2(0.13, 0.79)) * 0.64, 0.14 + temperatureHash(seedVector.zy + vec2(0.47, 0.41)) * 0.7);
|
|
582
|
+
vec2 randomCenter4 = vec2(0.18 + temperatureHash(seedVector.yy + vec2(0.89, 0.67)) * 0.64, 0.14 + temperatureHash(seedVector.xx + vec2(0.31, 0.83)) * 0.7);
|
|
583
|
+
vec2 randomCenter5 = vec2(0.18 + temperatureHash(seedVector.ww + vec2(0.57, 0.97)) * 0.64, 0.14 + temperatureHash(seedVector.zz + vec2(0.71, 0.59)) * 0.7);
|
|
584
|
+
float value = 0.0;
|
|
585
|
+
value += randomBlob(st, randomCenter0, 0.11 + temperatureHash(randomCenter0) * 0.1) * (0.55 + temperatureHash(randomCenter0.yx) * 0.45);
|
|
586
|
+
value += randomBlob(st, randomCenter1, 0.08 + temperatureHash(randomCenter1) * 0.09) * (0.42 + temperatureHash(randomCenter1.yx) * 0.45);
|
|
587
|
+
value += randomBlob(st, randomCenter2, 0.1 + temperatureHash(randomCenter2) * 0.08) * (0.38 + temperatureHash(randomCenter2.yx) * 0.4);
|
|
588
|
+
value -= randomBlob(st, randomCenter3, 0.12 + temperatureHash(randomCenter3) * 0.08) * (0.22 + temperatureHash(randomCenter3.yx) * 0.28);
|
|
589
|
+
value -= randomBlob(st, randomCenter4, 0.08 + temperatureHash(randomCenter4) * 0.08) * (0.18 + temperatureHash(randomCenter4.yx) * 0.2);
|
|
590
|
+
value += randomBlob(st, randomCenter5, 0.07 + temperatureHash(randomCenter5) * 0.06) * (0.18 + temperatureHash(randomCenter5.yx) * 0.22);
|
|
591
|
+
return value;
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
float sampleInfluence(vec2 st, vec4 samplePoint)
|
|
595
|
+
{
|
|
596
|
+
vec2 offset = st - samplePoint.xy;
|
|
597
|
+
float distanceSquared = dot(offset, offset);
|
|
598
|
+
float radius = 0.006 + samplePoint.w * 0.038;
|
|
599
|
+
return exp(-distanceSquared / radius);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
float sampleContribution(vec2 st, vec4 samplePoint, float index, inout float weight)
|
|
603
|
+
{
|
|
604
|
+
float enabled = step(index + 0.5, sampleCount);
|
|
605
|
+
float influence = sampleInfluence(st, samplePoint) * enabled;
|
|
606
|
+
weight += influence;
|
|
607
|
+
return samplePoint.z * influence;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
float sampleField(vec2 st)
|
|
611
|
+
{
|
|
612
|
+
float weight = 0.0;
|
|
613
|
+
float value = 0.0;
|
|
614
|
+
value += sampleContribution(st, sample0, 0.0, weight);
|
|
615
|
+
value += sampleContribution(st, sample1, 1.0, weight);
|
|
616
|
+
value += sampleContribution(st, sample2, 2.0, weight);
|
|
617
|
+
value += sampleContribution(st, sample3, 3.0, weight);
|
|
618
|
+
value += sampleContribution(st, sample4, 4.0, weight);
|
|
619
|
+
value += sampleContribution(st, sample5, 5.0, weight);
|
|
620
|
+
value += sampleContribution(st, sample6, 6.0, weight);
|
|
621
|
+
value += sampleContribution(st, sample7, 7.0, weight);
|
|
622
|
+
value += sampleContribution(st, sample8, 8.0, weight);
|
|
623
|
+
value += sampleContribution(st, sample9, 9.0, weight);
|
|
624
|
+
value += sampleContribution(st, sample10, 10.0, weight);
|
|
625
|
+
value += sampleContribution(st, sample11, 11.0, weight);
|
|
626
|
+
value += sampleContribution(st, sample12, 12.0, weight);
|
|
627
|
+
value += sampleContribution(st, sample13, 13.0, weight);
|
|
628
|
+
value += sampleContribution(st, sample14, 14.0, weight);
|
|
629
|
+
value += sampleContribution(st, sample15, 15.0, weight);
|
|
630
|
+
float base = 0.26 + randomField(st) * 0.12;
|
|
631
|
+
return mix(base, value / max(weight, 0.0001), smoothstep(0.01, 0.22, weight));
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
float riskField(vec2 st)
|
|
635
|
+
{
|
|
636
|
+
float broadNoise = temperatureNoise(st * 3.0 + seedVector.zw * 5.0);
|
|
637
|
+
float detailNoise = temperatureNoise(st * 9.0 + seedVector.wx * 7.0);
|
|
638
|
+
float hotSpot0 = exp(-dot(st - hotSpot0Center, st - hotSpot0Center) / max(0.0001, hotSpot0Radius));
|
|
639
|
+
float hotSpot1 = exp(-dot(st - hotSpot1Center, st - hotSpot1Center) / max(0.0001, hotSpot1Radius));
|
|
640
|
+
float coldSpot0 = exp(-dot(st - coldSpot0Center, st - coldSpot0Center) / max(0.0001, coldSpot0Radius));
|
|
641
|
+
float randomTemperature = 0.28 + randomField(st) * 0.56 + (broadNoise - 0.5) * 0.34 * noiseStrength + (detailNoise - 0.5) * 0.14 * noiseStrength;
|
|
642
|
+
randomTemperature += hotSpot0 * hotSpot0Strength + hotSpot1 * hotSpot1Strength;
|
|
643
|
+
randomTemperature -= coldSpot0 * coldSpot0Strength;
|
|
644
|
+
float sampleTexture = (broadNoise - 0.5) * 0.08 * noiseStrength + (detailNoise - 0.5) * 0.04 * noiseStrength;
|
|
645
|
+
float sampleTemperature = sampleField(st) + sampleTexture;
|
|
646
|
+
float hasSamples = step(0.5, sampleCount);
|
|
647
|
+
float value = mix(randomTemperature, sampleTemperature, hasSamples);
|
|
648
|
+
return clamp(value, 0.0, 1.0);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
czm_material czm_getMaterial(czm_materialInput materialInput)
|
|
652
|
+
{
|
|
653
|
+
czm_material material = czm_getDefaultMaterial(materialInput);
|
|
654
|
+
vec2 st = materialInput.st;
|
|
655
|
+
float value = riskField(st);
|
|
656
|
+
vec3 ramp = temperatureRamp(value);
|
|
657
|
+
float contour = smoothstep(0.03, 0.0, abs(fract(value * 8.0) - 0.5)) * contourStrength * contourLines;
|
|
658
|
+
float edgeFade = smoothstep(0.0, 0.035, st.x) * smoothstep(0.0, 0.035, st.y) * smoothstep(0.0, 0.035, 1.0 - st.x) * smoothstep(0.0, 0.035, 1.0 - st.y);
|
|
659
|
+
vec3 veil = mix(ramp, vec3(1.0, 0.96, 0.72), 0.08);
|
|
660
|
+
material.diffuse = veil * (0.82 + contour * 0.18);
|
|
661
|
+
material.emission = ramp * (0.12 + value * 0.16) + vec3(1.0, 0.88, 0.45) * contour * 0.12;
|
|
662
|
+
material.alpha = opacity * edgeFade * (0.52 + value * 0.24 + contour * 0.14);
|
|
663
|
+
return material;
|
|
664
|
+
}
|
|
665
|
+
`;
|
|
666
|
+
}
|
|
667
|
+
export function buildWaterSurfaceMaterialSource() {
|
|
668
|
+
return `
|
|
669
|
+
// ${GEO_WATER_SURFACE_MATERIAL_TYPE}
|
|
670
|
+
float waterHash(vec2 p)
|
|
671
|
+
{
|
|
672
|
+
return fract(sin(dot(p, vec2(127.1, 311.7))) * 43758.5453123);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
float waterNoise(vec2 p)
|
|
676
|
+
{
|
|
677
|
+
vec2 i = floor(p);
|
|
678
|
+
vec2 f = fract(p);
|
|
679
|
+
vec2 u = f * f * (3.0 - 2.0 * f);
|
|
680
|
+
float a = waterHash(i);
|
|
681
|
+
float b = waterHash(i + vec2(1.0, 0.0));
|
|
682
|
+
float c = waterHash(i + vec2(0.0, 1.0));
|
|
683
|
+
float d = waterHash(i + vec2(1.0, 1.0));
|
|
684
|
+
return mix(mix(a, b, u.x), mix(c, d, u.x), u.y);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
float waterHeight(vec2 uv, vec2 flow, float time)
|
|
688
|
+
{
|
|
689
|
+
vec2 crossFlow = vec2(-flow.y, flow.x);
|
|
690
|
+
float layerA = sin(dot(uv + flow * time * 0.055, flow) * 62.0 + time * 1.7);
|
|
691
|
+
float layerB = sin(dot(uv - crossFlow * time * 0.04, crossFlow) * 84.0 - time * 1.15);
|
|
692
|
+
float layerC = waterNoise(uv * 18.0 + flow * time * 1.4) * 2.0 - 1.0;
|
|
693
|
+
return layerA * 0.42 + layerB * 0.34 + layerC * 0.24;
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
vec3 getProceduralWaterNormal(vec2 uv, vec2 flow, float time, float strength)
|
|
697
|
+
{
|
|
698
|
+
float sampleStep = 0.006;
|
|
699
|
+
float center = waterHeight(uv, flow, time);
|
|
700
|
+
float dx = waterHeight(uv + vec2(sampleStep, 0.0), flow, time) - center;
|
|
701
|
+
float dy = waterHeight(uv + vec2(0.0, sampleStep), flow, time) - center;
|
|
702
|
+
return normalize(vec3(-dx * strength, -dy * strength, 1.0));
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
czm_material czm_getMaterial(czm_materialInput materialInput)
|
|
706
|
+
{
|
|
707
|
+
czm_material material = czm_getDefaultMaterial(materialInput);
|
|
708
|
+
vec2 st = materialInput.st;
|
|
709
|
+
float time = czm_frameNumber * 0.016667 * speed;
|
|
710
|
+
float direction = radians(flowDirection);
|
|
711
|
+
vec2 flow = vec2(cos(direction), sin(direction));
|
|
712
|
+
vec2 crossFlow = vec2(-flow.y, flow.x);
|
|
713
|
+
vec2 distortion = (flow * sin(dot(st, crossFlow) * 18.0 + time * 0.8) + crossFlow * sin(dot(st, flow) * 22.0 - time * 0.65)) * distortionScale * 0.00085;
|
|
714
|
+
vec2 moving = st + flow * time * 0.06 + distortion;
|
|
715
|
+
vec3 surfaceNormal = getProceduralWaterNormal(moving * (1.0 + waveStrength * 1.8), flow, time, mix(8.0, 28.0, waveStrength));
|
|
716
|
+
float waveA = waterHeight(moving * 1.8, flow, time) * 0.5 + 0.5;
|
|
717
|
+
float waveB = waterHeight(moving.yx * 2.2 + vec2(0.17, 0.31), crossFlow, time * 0.82) * 0.5 + 0.5;
|
|
718
|
+
float smallWave = clamp(dot(surfaceNormal.xy, vec2(0.72, -0.48)) * 0.5 + 0.5, 0.0, 1.0);
|
|
719
|
+
float riverEnabled = 1.0 - step(1.5, waterType);
|
|
720
|
+
float lakeEnabled = step(1.5, waterType) * (1.0 - step(2.5, waterType));
|
|
721
|
+
float floodEnabled = step(2.5, waterType);
|
|
722
|
+
float riverFlow = smoothstep(0.38, 1.0, waterNoise(moving * 9.0 + flow * time * 0.55)) * waveA;
|
|
723
|
+
float lakeShimmer = pow(waveA * waveB, 2.0);
|
|
724
|
+
float floodPulse = smoothstep(0.08, 0.0, abs(fract(distance(st, vec2(0.5)) * 4.0 - time * 0.45) - 0.5));
|
|
725
|
+
float riverWave = mix(waveA, max(riverFlow, waveB * 0.72), 0.45);
|
|
726
|
+
float wave = mix(waveA, max(riverWave, lakeShimmer), waveStrength);
|
|
727
|
+
wave = max(wave, floodPulse * floodEnabled);
|
|
728
|
+
vec3 viewDirection = normalize(materialInput.positionToEyeEC);
|
|
729
|
+
float viewFacing = clamp(1.0 - abs(dot(normalize(materialInput.normalEC), -viewDirection)), 0.0, 1.0);
|
|
730
|
+
float fresnel = pow(viewFacing, fresnelPower) * reflectivity;
|
|
731
|
+
float reflection = (0.18 + smallWave * 0.82) * reflectionStrength;
|
|
732
|
+
vec3 riverTint = mix(color.rgb, vec3(0.18, 0.72, 1.0), 0.24);
|
|
733
|
+
vec3 lakeTint = mix(color.rgb, vec3(0.36, 0.92, 0.86), 0.22);
|
|
734
|
+
vec3 floodTint = mix(color.rgb, vec3(0.42, 0.68, 1.0), 0.36);
|
|
735
|
+
vec3 waterColor = riverTint * riverEnabled + lakeTint * lakeEnabled + floodTint * floodEnabled;
|
|
736
|
+
vec3 deepColor = mix(waterColor, vec3(0.015, 0.11, 0.18), 0.46);
|
|
737
|
+
vec3 refractionColor = mix(deepColor, waterColor, 0.42 + wave * 0.34);
|
|
738
|
+
refractionColor += vec3(0.02, 0.12, 0.16) * refractionStrength * (0.5 + waveB * 0.5);
|
|
739
|
+
vec3 skyReflection = mix(vec3(0.58, 0.86, 1.0), vec3(1.0), pow(smallWave, 5.0));
|
|
740
|
+
vec3 reflectionColor = skyReflection * (0.32 + reflection * 0.78);
|
|
741
|
+
vec3 finalColor = mix(refractionColor, reflectionColor, clamp(fresnel + reflection * 0.24, 0.0, 1.0));
|
|
742
|
+
float foam = smoothstep(0.68, 1.0, wave) * smoothstep(0.38, 1.0, waveStrength);
|
|
743
|
+
finalColor = mix(finalColor, vec3(0.82, 0.98, 1.0), foam * 0.18);
|
|
744
|
+
material.normal = surfaceNormal;
|
|
745
|
+
material.diffuse = finalColor * (0.56 + wave * 0.18);
|
|
746
|
+
material.emission = reflectionColor * (0.14 + fresnel * 0.42) + waterColor * ((riverFlow * 0.08 + waveB * 0.06) * riverEnabled + floodPulse * floodEnabled * 0.22);
|
|
747
|
+
material.alpha = opacity * (0.62 + wave * 0.12 + fresnel * 0.2 + refractionStrength * 0.06);
|
|
748
|
+
return material;
|
|
749
|
+
}
|
|
750
|
+
`;
|
|
751
|
+
}
|
|
752
|
+
export function buildLightWallMaterialSource() {
|
|
753
|
+
return `
|
|
754
|
+
// ${GEO_LIGHT_WALL_MATERIAL_TYPE}
|
|
755
|
+
czm_material czm_getMaterial(czm_materialInput materialInput)
|
|
756
|
+
{
|
|
757
|
+
czm_material material = czm_getDefaultMaterial(materialInput);
|
|
758
|
+
vec2 st = materialInput.st;
|
|
759
|
+
float time = fract((czm_frameNumber * 0.016667 * speed));
|
|
760
|
+
float verticalFade = smoothstep(0.0, 0.12, st.t) * (1.0 - smoothstep(0.88, 1.0, st.t));
|
|
761
|
+
float flowLine = smoothstep(0.035, 0.0, abs(fract(st.t * 3.0 - time) - 0.5));
|
|
762
|
+
float scanLine = smoothstep(0.018, 0.0, abs(fract(st.t * scanLineCount - time * 1.8) - 0.5));
|
|
763
|
+
float breath = mix(1.0, 0.58 + 0.42 * sin(time * 6.28318530718), breathing);
|
|
764
|
+
float verticalStripe = smoothstep(0.012, 0.0, abs(fract(st.s * 18.0 + time * 0.6) - 0.5));
|
|
765
|
+
float spark = smoothstep(0.02, 0.0, abs(fract((st.s + st.t) * 12.0 - time * 2.2) - 0.5));
|
|
766
|
+
float securityEnabled = 1.0 - step(1.5, wallType);
|
|
767
|
+
float warningEnabled = step(1.5, wallType) * (1.0 - step(2.5, wallType));
|
|
768
|
+
float dataEnabled = step(2.5, wallType) * (1.0 - step(3.5, wallType));
|
|
769
|
+
float fenceEnabled = step(3.5, wallType) * (1.0 - step(4.5, wallType));
|
|
770
|
+
float pulseEnabled = step(4.5, wallType);
|
|
771
|
+
float securityWall = flowLine * 0.72 + scanLine * 0.42;
|
|
772
|
+
float warningWall = max(scanLine * 0.95, step(0.5, fract(st.s * 10.0 + st.t * 4.0 - time)) * 0.12);
|
|
773
|
+
float dataWall = verticalStripe * 0.55 + spark * 0.75 + scanLine * 0.24;
|
|
774
|
+
float fenceWall = max(verticalStripe, smoothstep(0.01, 0.0, abs(fract(st.t * 8.0) - 0.5))) * 0.54;
|
|
775
|
+
float pulseWall = flowLine * 1.05 + smoothstep(0.2, 0.0, abs(st.t - time)) * 0.82;
|
|
776
|
+
float style = securityWall * securityEnabled + warningWall * warningEnabled + dataWall * dataEnabled + fenceWall * fenceEnabled + pulseWall * pulseEnabled;
|
|
777
|
+
vec3 warningTint = mix(color.rgb, vec3(1.0, 0.48, 0.1), 0.44);
|
|
778
|
+
vec3 dataTint = mix(color.rgb, vec3(0.36, 0.74, 1.0), 0.38);
|
|
779
|
+
vec3 fenceTint = mix(color.rgb, vec3(0.74, 1.0, 0.42), 0.3);
|
|
780
|
+
vec3 pulseTint = mix(color.rgb, vec3(1.0, 0.18, 0.72), 0.42);
|
|
781
|
+
vec3 styleColor = color.rgb * securityEnabled + warningTint * warningEnabled + dataTint * dataEnabled + fenceTint * fenceEnabled + pulseTint * pulseEnabled;
|
|
782
|
+
float rim = smoothstep(0.025, 0.0, min(st.t, 1.0 - st.t));
|
|
783
|
+
material.diffuse = styleColor * (0.16 + verticalFade * 0.18 + style * 0.24);
|
|
784
|
+
material.emission = styleColor * (style * (0.9 + pulseEnabled * 0.45) + rim * 0.48) * breath;
|
|
785
|
+
material.alpha = opacity * (0.12 * verticalFade + style * 0.62 + rim * 0.42) * breath;
|
|
786
|
+
return material;
|
|
787
|
+
}
|
|
788
|
+
`;
|
|
789
|
+
}
|
|
790
|
+
export function buildScanConeMaterialSource() {
|
|
791
|
+
return `
|
|
792
|
+
// ${GEO_SCAN_CONE_MATERIAL_TYPE}
|
|
793
|
+
czm_material czm_getMaterial(czm_materialInput materialInput)
|
|
794
|
+
{
|
|
795
|
+
czm_material material = czm_getDefaultMaterial(materialInput);
|
|
796
|
+
vec2 st = materialInput.st;
|
|
797
|
+
vec2 centered = st - vec2(0.5);
|
|
798
|
+
float radius = length(centered) * 2.0;
|
|
799
|
+
float angle = atan(centered.y, centered.x) / 6.28318530718 + 0.5;
|
|
800
|
+
float time = fract((czm_frameNumber * 0.016667 * speed));
|
|
801
|
+
float verticalFade = smoothstep(0.0, 0.12, st.t) * (1.0 - smoothstep(0.92, 1.0, st.t));
|
|
802
|
+
float sweepBand = smoothstep(0.08, 0.0, abs(fract(angle - time) - 0.5));
|
|
803
|
+
float radialGrid = smoothstep(0.012, 0.0, abs(fract(radius * 7.0 - time * 2.0) - 0.5));
|
|
804
|
+
float verticalGrid = smoothstep(0.01, 0.0, abs(fract(st.t * 9.0 + time) - 0.5));
|
|
805
|
+
float searchlightEnabled = 1.0 - step(1.5, coneType);
|
|
806
|
+
float radarEnabled = step(1.5, coneType) * (1.0 - step(2.5, coneType));
|
|
807
|
+
float cameraEnabled = step(2.5, coneType) * (1.0 - step(3.5, coneType));
|
|
808
|
+
float droneEnabled = step(3.5, coneType) * (1.0 - step(4.5, coneType));
|
|
809
|
+
float alarmEnabled = step(4.5, coneType);
|
|
810
|
+
float searchlightCone = smoothstep(0.82, 0.0, radius) * (0.32 + sweepBand * 0.8);
|
|
811
|
+
float radarCone = radialGrid * 0.55 + sweepBand * 0.82 + verticalGrid * 0.2;
|
|
812
|
+
float cameraCone = smoothstep(0.025, 0.0, abs(centered.x)) * 0.42 + sweepBand * 0.62;
|
|
813
|
+
float droneCone = (radialGrid + verticalGrid) * 0.35 + smoothstep(0.05, 0.0, abs(radius - 0.72)) * 0.7;
|
|
814
|
+
float alarmCone = max(sweepBand, smoothstep(0.018, 0.0, abs(fract(angle * 12.0 + time * 4.0) - 0.5))) * 0.95;
|
|
815
|
+
float style = searchlightCone * searchlightEnabled + radarCone * radarEnabled + cameraCone * cameraEnabled + droneCone * droneEnabled + alarmCone * alarmEnabled;
|
|
816
|
+
vec3 radarTint = mix(color.rgb, vec3(0.35, 1.0, 0.46), 0.32);
|
|
817
|
+
vec3 cameraTint = mix(color.rgb, vec3(0.42, 0.68, 1.0), 0.3);
|
|
818
|
+
vec3 droneTint = mix(color.rgb, vec3(0.8, 0.55, 1.0), 0.36);
|
|
819
|
+
vec3 alarmTint = mix(color.rgb, vec3(1.0, 0.12, 0.26), 0.46);
|
|
820
|
+
vec3 styleColor = color.rgb * searchlightEnabled + radarTint * radarEnabled + cameraTint * cameraEnabled + droneTint * droneEnabled + alarmTint * alarmEnabled;
|
|
821
|
+
float edge = smoothstep(0.05, 0.0, abs(radius - 1.0));
|
|
822
|
+
material.diffuse = styleColor * (0.12 + style * 0.2);
|
|
823
|
+
material.emission = styleColor * (style * 1.15 + edge * 0.54) * verticalFade;
|
|
824
|
+
material.alpha = opacity * verticalFade * (0.08 + style * 0.62 + edge * 0.36);
|
|
825
|
+
return material;
|
|
826
|
+
}
|
|
827
|
+
`;
|
|
828
|
+
}
|
|
829
|
+
export function buildShieldDomeMaterialSource() {
|
|
830
|
+
return `
|
|
831
|
+
// ${GEO_SHIELD_DOME_MATERIAL_TYPE}
|
|
832
|
+
czm_material czm_getMaterial(czm_materialInput materialInput)
|
|
833
|
+
{
|
|
834
|
+
czm_material material = czm_getDefaultMaterial(materialInput);
|
|
835
|
+
vec2 st = materialInput.st;
|
|
836
|
+
float time = fract((czm_frameNumber * 0.016667 * speed));
|
|
837
|
+
float lat = st.t;
|
|
838
|
+
float lon = st.s;
|
|
839
|
+
float domeFade = smoothstep(0.02, 0.16, lat) * (1.0 - smoothstep(0.92, 1.0, lat));
|
|
840
|
+
float gridLine = max(
|
|
841
|
+
smoothstep(0.012, 0.0, abs(fract(lon * gridDensity + lat * 0.5) - 0.5)),
|
|
842
|
+
smoothstep(0.012, 0.0, abs(fract(lat * gridDensity * 0.55) - 0.5))
|
|
843
|
+
);
|
|
844
|
+
float scanLine = smoothstep(0.035, 0.0, abs(fract(lat * 1.2 - time) - 0.5));
|
|
845
|
+
float energyPulse = smoothstep(0.08, 0.0, abs(fract(distance(st, vec2(0.5, 0.0)) * 2.0 - time * 1.6) - 0.5)) * pulseStrength;
|
|
846
|
+
float hexEnabled = 1.0 - step(1.5, domeType);
|
|
847
|
+
float plasmaEnabled = step(1.5, domeType) * (1.0 - step(2.5, domeType));
|
|
848
|
+
float matrixEnabled = step(2.5, domeType) * (1.0 - step(3.5, domeType));
|
|
849
|
+
float aegisEnabled = step(3.5, domeType) * (1.0 - step(4.5, domeType));
|
|
850
|
+
float stormEnabled = step(4.5, domeType);
|
|
851
|
+
float hexDome = gridLine * 0.7 + scanLine * 0.48;
|
|
852
|
+
float plasmaDome = energyPulse * 1.05 + sin((lon * 36.0 + lat * 18.0 + time * 8.0)) * 0.08;
|
|
853
|
+
float matrixDome = gridLine * 0.42 + smoothstep(0.018, 0.0, abs(fract(lon * 28.0 - time * 3.0) - 0.5)) * 0.78;
|
|
854
|
+
float aegisDome = max(gridLine * 0.52, scanLine * 0.72) + smoothstep(0.03, 0.0, abs(lat - 0.08)) * 0.54;
|
|
855
|
+
float stormDome = energyPulse * 0.82 + smoothstep(0.02, 0.0, abs(fract((lon + lat) * 18.0 + time * 5.0) - 0.5)) * 0.82;
|
|
856
|
+
float style = hexDome * hexEnabled + plasmaDome * plasmaEnabled + matrixDome * matrixEnabled + aegisDome * aegisEnabled + stormDome * stormEnabled;
|
|
857
|
+
vec3 plasmaTint = mix(color.rgb, vec3(1.0, 0.28, 0.86), 0.4);
|
|
858
|
+
vec3 matrixTint = mix(color.rgb, vec3(0.62, 1.0, 0.24), 0.42);
|
|
859
|
+
vec3 aegisTint = mix(color.rgb, vec3(0.46, 0.78, 1.0), 0.28);
|
|
860
|
+
vec3 stormTint = mix(color.rgb, vec3(0.82, 0.55, 1.0), 0.38);
|
|
861
|
+
vec3 styleColor = color.rgb * hexEnabled + plasmaTint * plasmaEnabled + matrixTint * matrixEnabled + aegisTint * aegisEnabled + stormTint * stormEnabled;
|
|
862
|
+
float rim = smoothstep(0.06, 0.0, lat) + smoothstep(0.035, 0.0, abs(lat - 0.96));
|
|
863
|
+
material.diffuse = styleColor * (0.14 + style * 0.18);
|
|
864
|
+
material.emission = styleColor * (style * 1.05 + rim * 0.62 + energyPulse * 0.48) * domeFade;
|
|
865
|
+
material.alpha = opacity * domeFade * (0.09 + style * 0.55 + rim * 0.36 + energyPulse * 0.22);
|
|
866
|
+
return material;
|
|
867
|
+
}
|
|
868
|
+
`;
|
|
869
|
+
}
|
|
870
|
+
export class RadarScanEffect {
|
|
871
|
+
viewer;
|
|
872
|
+
dataSource;
|
|
873
|
+
options;
|
|
874
|
+
primitive = null;
|
|
875
|
+
centerEntity = null;
|
|
876
|
+
renderFrame = 0;
|
|
877
|
+
destroyed = false;
|
|
878
|
+
constructor(viewer, options) {
|
|
879
|
+
this.viewer = viewer;
|
|
880
|
+
this.options = normalizeRadarScanOptions(options);
|
|
881
|
+
this.dataSource = new CustomDataSource('geo-effect-kit-radar-scan');
|
|
882
|
+
this.viewer.dataSources.add(this.dataSource);
|
|
883
|
+
this.renderPrimitive();
|
|
884
|
+
this.syncCenterEntity();
|
|
885
|
+
this.startRenderLoop();
|
|
886
|
+
this.viewer.scene.requestRender();
|
|
887
|
+
}
|
|
888
|
+
update(options) {
|
|
889
|
+
if (this.destroyed)
|
|
890
|
+
return;
|
|
891
|
+
const next = normalizeRadarScanOptions({
|
|
892
|
+
...this.options,
|
|
893
|
+
...options,
|
|
894
|
+
center: options.center ?? this.options.center,
|
|
895
|
+
radiusMeters: options.radiusMeters ?? this.options.radiusMeters,
|
|
896
|
+
});
|
|
897
|
+
const rebuildPrimitive = shouldRebuildRadarScan(this.options, next);
|
|
898
|
+
this.options = next;
|
|
899
|
+
if (rebuildPrimitive) {
|
|
900
|
+
this.renderPrimitive();
|
|
901
|
+
}
|
|
902
|
+
else {
|
|
903
|
+
this.applyPrimitiveOptions();
|
|
904
|
+
}
|
|
905
|
+
this.syncCenterEntity();
|
|
906
|
+
if (this.options.visible)
|
|
907
|
+
this.startRenderLoop();
|
|
908
|
+
this.viewer.scene.requestRender();
|
|
909
|
+
}
|
|
910
|
+
show() {
|
|
911
|
+
if (this.destroyed)
|
|
912
|
+
return;
|
|
913
|
+
this.options = { ...this.options, visible: true };
|
|
914
|
+
this.applyPrimitiveOptions();
|
|
915
|
+
if (this.centerEntity)
|
|
916
|
+
this.centerEntity.show = this.options.showCenter;
|
|
917
|
+
this.startRenderLoop();
|
|
918
|
+
this.viewer.scene.requestRender();
|
|
919
|
+
}
|
|
920
|
+
hide() {
|
|
921
|
+
if (this.destroyed)
|
|
922
|
+
return;
|
|
923
|
+
this.options = { ...this.options, visible: false };
|
|
924
|
+
this.applyPrimitiveOptions();
|
|
925
|
+
if (this.centerEntity)
|
|
926
|
+
this.centerEntity.show = false;
|
|
927
|
+
this.stopRenderLoop();
|
|
928
|
+
this.viewer.scene.requestRender();
|
|
929
|
+
}
|
|
930
|
+
flyTo(options = {}) {
|
|
931
|
+
if (this.destroyed)
|
|
932
|
+
return;
|
|
933
|
+
const center = this.getCenterCartesian();
|
|
934
|
+
const sphere = new BoundingSphere(center, this.options.radiusMeters);
|
|
935
|
+
this.viewer.camera.flyToBoundingSphere(sphere, {
|
|
936
|
+
duration: options.duration ?? 1,
|
|
937
|
+
offset: new HeadingPitchRange(0, options.pitch ?? -0.72, this.options.radiusMeters * (options.rangeMultiplier ?? 2.25)),
|
|
938
|
+
});
|
|
939
|
+
}
|
|
940
|
+
destroy() {
|
|
941
|
+
if (this.destroyed)
|
|
942
|
+
return;
|
|
943
|
+
this.destroyed = true;
|
|
944
|
+
this.stopRenderLoop();
|
|
945
|
+
this.removePrimitive();
|
|
946
|
+
if (this.centerEntity) {
|
|
947
|
+
this.dataSource.entities.remove(this.centerEntity);
|
|
948
|
+
this.centerEntity = null;
|
|
949
|
+
}
|
|
950
|
+
this.dataSource.entities.removeAll();
|
|
951
|
+
this.viewer.dataSources.remove(this.dataSource, true);
|
|
952
|
+
this.viewer.scene.requestRender();
|
|
953
|
+
}
|
|
954
|
+
isVisible() {
|
|
955
|
+
return this.options.visible;
|
|
956
|
+
}
|
|
957
|
+
isDestroyed() {
|
|
958
|
+
return this.destroyed;
|
|
959
|
+
}
|
|
960
|
+
getOptions() {
|
|
961
|
+
return {
|
|
962
|
+
...this.options,
|
|
963
|
+
center: { ...this.options.center },
|
|
964
|
+
};
|
|
965
|
+
}
|
|
966
|
+
renderPrimitive() {
|
|
967
|
+
this.removePrimitive();
|
|
968
|
+
registerRadarScanMaterial(this.options);
|
|
969
|
+
const instance = new GeometryInstance({
|
|
970
|
+
geometry: new CircleGeometry({
|
|
971
|
+
center: this.getCenterCartesian(),
|
|
972
|
+
radius: this.options.radiusMeters,
|
|
973
|
+
vertexFormat: EllipsoidSurfaceAppearance.VERTEX_FORMAT,
|
|
974
|
+
}),
|
|
975
|
+
});
|
|
976
|
+
this.primitive = this.viewer.scene.primitives.add(new GroundPrimitive({
|
|
977
|
+
geometryInstances: instance,
|
|
978
|
+
appearance: new EllipsoidSurfaceAppearance({
|
|
979
|
+
material: createRadarScanMaterial(this.options),
|
|
980
|
+
}),
|
|
981
|
+
show: this.options.visible,
|
|
982
|
+
}));
|
|
983
|
+
}
|
|
984
|
+
removePrimitive() {
|
|
985
|
+
if (!this.primitive)
|
|
986
|
+
return;
|
|
987
|
+
this.viewer.scene.primitives.remove(this.primitive);
|
|
988
|
+
this.primitive = null;
|
|
989
|
+
}
|
|
990
|
+
applyPrimitiveOptions() {
|
|
991
|
+
if (this.primitive) {
|
|
992
|
+
this.primitive.show = this.options.visible;
|
|
993
|
+
this.applyMaterialOptions();
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
applyMaterialOptions() {
|
|
997
|
+
const material = this.primitive?.appearance?.material;
|
|
998
|
+
if (!material)
|
|
999
|
+
return;
|
|
1000
|
+
material.uniforms.color = Color.fromCssColorString(this.options.color).withAlpha(1);
|
|
1001
|
+
material.uniforms.opacity = this.options.opacity;
|
|
1002
|
+
material.uniforms.ringsEnabled = this.options.rings ? 1 : 0;
|
|
1003
|
+
material.uniforms.radarType = getRadarScanTypeUniform(this.options.type);
|
|
1004
|
+
material.uniforms.scanDurationMs = this.options.scanDurationMs;
|
|
1005
|
+
}
|
|
1006
|
+
syncCenterEntity() {
|
|
1007
|
+
if (!this.options.showCenter) {
|
|
1008
|
+
if (this.centerEntity) {
|
|
1009
|
+
this.dataSource.entities.remove(this.centerEntity);
|
|
1010
|
+
this.centerEntity = null;
|
|
1011
|
+
}
|
|
1012
|
+
return;
|
|
1013
|
+
}
|
|
1014
|
+
const position = this.getCenterCartesian();
|
|
1015
|
+
const color = Color.fromCssColorString(this.options.color);
|
|
1016
|
+
if (!this.centerEntity) {
|
|
1017
|
+
this.centerEntity = this.dataSource.entities.add({
|
|
1018
|
+
id: 'geo-effect-kit-radar-scan-center',
|
|
1019
|
+
position,
|
|
1020
|
+
show: this.options.visible,
|
|
1021
|
+
point: new PointGraphics({
|
|
1022
|
+
pixelSize: 12,
|
|
1023
|
+
color: new ConstantProperty(color),
|
|
1024
|
+
outlineColor: new ConstantProperty(Color.WHITE.withAlpha(0.72)),
|
|
1025
|
+
outlineWidth: 2,
|
|
1026
|
+
heightReference: HeightReference.CLAMP_TO_GROUND,
|
|
1027
|
+
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
|
1028
|
+
}),
|
|
1029
|
+
});
|
|
1030
|
+
return;
|
|
1031
|
+
}
|
|
1032
|
+
this.centerEntity.position = new ConstantPositionProperty(position);
|
|
1033
|
+
this.centerEntity.show = this.options.visible;
|
|
1034
|
+
if (this.centerEntity.point) {
|
|
1035
|
+
this.centerEntity.point.color = new ConstantProperty(color);
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
startRenderLoop() {
|
|
1039
|
+
if (this.renderFrame || !this.options.visible || typeof window === 'undefined')
|
|
1040
|
+
return;
|
|
1041
|
+
const tick = () => {
|
|
1042
|
+
if (this.destroyed || !this.primitive || !this.options.visible) {
|
|
1043
|
+
this.renderFrame = 0;
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
this.viewer.scene.requestRender();
|
|
1047
|
+
this.renderFrame = window.requestAnimationFrame(tick);
|
|
1048
|
+
};
|
|
1049
|
+
this.renderFrame = window.requestAnimationFrame(tick);
|
|
1050
|
+
}
|
|
1051
|
+
stopRenderLoop() {
|
|
1052
|
+
if (!this.renderFrame || typeof window === 'undefined') {
|
|
1053
|
+
this.renderFrame = 0;
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
window.cancelAnimationFrame(this.renderFrame);
|
|
1057
|
+
this.renderFrame = 0;
|
|
1058
|
+
}
|
|
1059
|
+
getCenterCartesian() {
|
|
1060
|
+
return Cartesian3.fromDegrees(this.options.center.longitude, this.options.center.latitude);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
export class RippleSpreadEffect {
|
|
1064
|
+
viewer;
|
|
1065
|
+
dataSource;
|
|
1066
|
+
options;
|
|
1067
|
+
primitive = null;
|
|
1068
|
+
centerEntity = null;
|
|
1069
|
+
renderFrame = 0;
|
|
1070
|
+
destroyed = false;
|
|
1071
|
+
constructor(viewer, options) {
|
|
1072
|
+
this.viewer = viewer;
|
|
1073
|
+
this.options = normalizeRippleSpreadOptions(options);
|
|
1074
|
+
this.dataSource = new CustomDataSource('geo-effect-kit-ripple-spread');
|
|
1075
|
+
this.viewer.dataSources.add(this.dataSource);
|
|
1076
|
+
this.renderPrimitive();
|
|
1077
|
+
this.syncCenterEntity();
|
|
1078
|
+
this.startRenderLoop();
|
|
1079
|
+
this.viewer.scene.requestRender();
|
|
1080
|
+
}
|
|
1081
|
+
update(options) {
|
|
1082
|
+
if (this.destroyed)
|
|
1083
|
+
return;
|
|
1084
|
+
const next = normalizeRippleSpreadOptions({
|
|
1085
|
+
...this.options,
|
|
1086
|
+
...options,
|
|
1087
|
+
center: options.center ?? this.options.center,
|
|
1088
|
+
radiusMeters: options.radiusMeters ?? this.options.radiusMeters,
|
|
1089
|
+
});
|
|
1090
|
+
const rebuildPrimitive = shouldRebuildRippleSpread(this.options, next);
|
|
1091
|
+
this.options = next;
|
|
1092
|
+
if (rebuildPrimitive) {
|
|
1093
|
+
this.renderPrimitive();
|
|
1094
|
+
}
|
|
1095
|
+
else {
|
|
1096
|
+
this.applyPrimitiveOptions();
|
|
1097
|
+
}
|
|
1098
|
+
this.syncCenterEntity();
|
|
1099
|
+
if (this.options.visible)
|
|
1100
|
+
this.startRenderLoop();
|
|
1101
|
+
this.viewer.scene.requestRender();
|
|
1102
|
+
}
|
|
1103
|
+
show() {
|
|
1104
|
+
if (this.destroyed)
|
|
1105
|
+
return;
|
|
1106
|
+
this.options = { ...this.options, visible: true };
|
|
1107
|
+
this.applyPrimitiveOptions();
|
|
1108
|
+
if (this.centerEntity)
|
|
1109
|
+
this.centerEntity.show = this.options.showCenter;
|
|
1110
|
+
this.startRenderLoop();
|
|
1111
|
+
this.viewer.scene.requestRender();
|
|
1112
|
+
}
|
|
1113
|
+
hide() {
|
|
1114
|
+
if (this.destroyed)
|
|
1115
|
+
return;
|
|
1116
|
+
this.options = { ...this.options, visible: false };
|
|
1117
|
+
this.applyPrimitiveOptions();
|
|
1118
|
+
if (this.centerEntity)
|
|
1119
|
+
this.centerEntity.show = false;
|
|
1120
|
+
this.stopRenderLoop();
|
|
1121
|
+
this.viewer.scene.requestRender();
|
|
1122
|
+
}
|
|
1123
|
+
flyTo(options = {}) {
|
|
1124
|
+
if (this.destroyed)
|
|
1125
|
+
return;
|
|
1126
|
+
const center = this.getCenterCartesian();
|
|
1127
|
+
const sphere = new BoundingSphere(center, this.options.radiusMeters);
|
|
1128
|
+
this.viewer.camera.flyToBoundingSphere(sphere, {
|
|
1129
|
+
duration: options.duration ?? 1,
|
|
1130
|
+
offset: new HeadingPitchRange(0, options.pitch ?? -0.72, this.options.radiusMeters * (options.rangeMultiplier ?? 2.25)),
|
|
1131
|
+
});
|
|
1132
|
+
}
|
|
1133
|
+
destroy() {
|
|
1134
|
+
if (this.destroyed)
|
|
1135
|
+
return;
|
|
1136
|
+
this.destroyed = true;
|
|
1137
|
+
this.stopRenderLoop();
|
|
1138
|
+
this.removePrimitive();
|
|
1139
|
+
if (this.centerEntity) {
|
|
1140
|
+
this.dataSource.entities.remove(this.centerEntity);
|
|
1141
|
+
this.centerEntity = null;
|
|
1142
|
+
}
|
|
1143
|
+
this.dataSource.entities.removeAll();
|
|
1144
|
+
this.viewer.dataSources.remove(this.dataSource, true);
|
|
1145
|
+
this.viewer.scene.requestRender();
|
|
1146
|
+
}
|
|
1147
|
+
isVisible() {
|
|
1148
|
+
return this.options.visible;
|
|
1149
|
+
}
|
|
1150
|
+
isDestroyed() {
|
|
1151
|
+
return this.destroyed;
|
|
1152
|
+
}
|
|
1153
|
+
getOptions() {
|
|
1154
|
+
return {
|
|
1155
|
+
...this.options,
|
|
1156
|
+
center: { ...this.options.center },
|
|
1157
|
+
};
|
|
1158
|
+
}
|
|
1159
|
+
renderPrimitive() {
|
|
1160
|
+
this.removePrimitive();
|
|
1161
|
+
registerRippleSpreadMaterial(this.options);
|
|
1162
|
+
const instance = new GeometryInstance({
|
|
1163
|
+
geometry: new CircleGeometry({
|
|
1164
|
+
center: this.getCenterCartesian(),
|
|
1165
|
+
radius: this.options.radiusMeters,
|
|
1166
|
+
vertexFormat: EllipsoidSurfaceAppearance.VERTEX_FORMAT,
|
|
1167
|
+
}),
|
|
1168
|
+
});
|
|
1169
|
+
this.primitive = this.viewer.scene.primitives.add(new GroundPrimitive({
|
|
1170
|
+
geometryInstances: instance,
|
|
1171
|
+
appearance: new EllipsoidSurfaceAppearance({
|
|
1172
|
+
material: createRippleSpreadMaterial(this.options),
|
|
1173
|
+
}),
|
|
1174
|
+
show: this.options.visible,
|
|
1175
|
+
}));
|
|
1176
|
+
}
|
|
1177
|
+
removePrimitive() {
|
|
1178
|
+
if (!this.primitive)
|
|
1179
|
+
return;
|
|
1180
|
+
this.viewer.scene.primitives.remove(this.primitive);
|
|
1181
|
+
this.primitive = null;
|
|
1182
|
+
}
|
|
1183
|
+
applyPrimitiveOptions() {
|
|
1184
|
+
if (this.primitive) {
|
|
1185
|
+
this.primitive.show = this.options.visible;
|
|
1186
|
+
this.applyMaterialOptions();
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
applyMaterialOptions() {
|
|
1190
|
+
const material = this.primitive?.appearance?.material;
|
|
1191
|
+
if (!material)
|
|
1192
|
+
return;
|
|
1193
|
+
material.uniforms.color = Color.fromCssColorString(this.options.color).withAlpha(1);
|
|
1194
|
+
material.uniforms.opacity = this.options.opacity;
|
|
1195
|
+
material.uniforms.rippleType = getRippleSpreadTypeUniform(this.options.type);
|
|
1196
|
+
material.uniforms.ringCount = this.options.ringCount;
|
|
1197
|
+
material.uniforms.durationMs = this.options.durationMs;
|
|
1198
|
+
}
|
|
1199
|
+
syncCenterEntity() {
|
|
1200
|
+
if (!this.options.showCenter) {
|
|
1201
|
+
if (this.centerEntity) {
|
|
1202
|
+
this.dataSource.entities.remove(this.centerEntity);
|
|
1203
|
+
this.centerEntity = null;
|
|
1204
|
+
}
|
|
1205
|
+
return;
|
|
1206
|
+
}
|
|
1207
|
+
const position = this.getCenterCartesian();
|
|
1208
|
+
const color = Color.fromCssColorString(this.options.color);
|
|
1209
|
+
if (!this.centerEntity) {
|
|
1210
|
+
this.centerEntity = this.dataSource.entities.add({
|
|
1211
|
+
id: 'geo-effect-kit-ripple-spread-center',
|
|
1212
|
+
position,
|
|
1213
|
+
show: this.options.visible,
|
|
1214
|
+
point: new PointGraphics({
|
|
1215
|
+
pixelSize: 12,
|
|
1216
|
+
color: new ConstantProperty(color),
|
|
1217
|
+
outlineColor: new ConstantProperty(Color.WHITE.withAlpha(0.72)),
|
|
1218
|
+
outlineWidth: 2,
|
|
1219
|
+
heightReference: HeightReference.CLAMP_TO_GROUND,
|
|
1220
|
+
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
|
1221
|
+
}),
|
|
1222
|
+
});
|
|
1223
|
+
return;
|
|
1224
|
+
}
|
|
1225
|
+
this.centerEntity.position = new ConstantPositionProperty(position);
|
|
1226
|
+
this.centerEntity.show = this.options.visible;
|
|
1227
|
+
if (this.centerEntity.point) {
|
|
1228
|
+
this.centerEntity.point.color = new ConstantProperty(color);
|
|
1229
|
+
}
|
|
1230
|
+
}
|
|
1231
|
+
startRenderLoop() {
|
|
1232
|
+
if (this.renderFrame || !this.options.visible || typeof window === 'undefined')
|
|
1233
|
+
return;
|
|
1234
|
+
const tick = () => {
|
|
1235
|
+
if (this.destroyed || !this.primitive || !this.options.visible) {
|
|
1236
|
+
this.renderFrame = 0;
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
this.viewer.scene.requestRender();
|
|
1240
|
+
this.renderFrame = window.requestAnimationFrame(tick);
|
|
1241
|
+
};
|
|
1242
|
+
this.renderFrame = window.requestAnimationFrame(tick);
|
|
1243
|
+
}
|
|
1244
|
+
stopRenderLoop() {
|
|
1245
|
+
if (!this.renderFrame || typeof window === 'undefined') {
|
|
1246
|
+
this.renderFrame = 0;
|
|
1247
|
+
return;
|
|
1248
|
+
}
|
|
1249
|
+
window.cancelAnimationFrame(this.renderFrame);
|
|
1250
|
+
this.renderFrame = 0;
|
|
1251
|
+
}
|
|
1252
|
+
getCenterCartesian() {
|
|
1253
|
+
return Cartesian3.fromDegrees(this.options.center.longitude, this.options.center.latitude);
|
|
1254
|
+
}
|
|
1255
|
+
}
|
|
1256
|
+
export class SceneWeatherEffect {
|
|
1257
|
+
viewer;
|
|
1258
|
+
options;
|
|
1259
|
+
stage = null;
|
|
1260
|
+
renderFrame = 0;
|
|
1261
|
+
destroyed = false;
|
|
1262
|
+
constructor(viewer, options = {}) {
|
|
1263
|
+
this.viewer = viewer;
|
|
1264
|
+
this.options = normalizeSceneWeatherOptions(options);
|
|
1265
|
+
this.renderStage();
|
|
1266
|
+
this.startRenderLoop();
|
|
1267
|
+
this.viewer.scene.requestRender();
|
|
1268
|
+
}
|
|
1269
|
+
update(options) {
|
|
1270
|
+
if (this.destroyed)
|
|
1271
|
+
return;
|
|
1272
|
+
this.options = normalizeSceneWeatherOptions({
|
|
1273
|
+
...this.options,
|
|
1274
|
+
...options,
|
|
1275
|
+
visible: options.visible ?? this.options.visible,
|
|
1276
|
+
});
|
|
1277
|
+
this.applyStageOptions();
|
|
1278
|
+
if (this.options.visible)
|
|
1279
|
+
this.startRenderLoop();
|
|
1280
|
+
else
|
|
1281
|
+
this.stopRenderLoop();
|
|
1282
|
+
this.viewer.scene.requestRender();
|
|
1283
|
+
}
|
|
1284
|
+
show() {
|
|
1285
|
+
if (this.destroyed)
|
|
1286
|
+
return;
|
|
1287
|
+
this.options = { ...this.options, visible: true };
|
|
1288
|
+
this.applyStageOptions();
|
|
1289
|
+
this.startRenderLoop();
|
|
1290
|
+
this.viewer.scene.requestRender();
|
|
1291
|
+
}
|
|
1292
|
+
hide() {
|
|
1293
|
+
if (this.destroyed)
|
|
1294
|
+
return;
|
|
1295
|
+
this.options = { ...this.options, visible: false };
|
|
1296
|
+
this.applyStageOptions();
|
|
1297
|
+
this.stopRenderLoop();
|
|
1298
|
+
this.viewer.scene.requestRender();
|
|
1299
|
+
}
|
|
1300
|
+
flyTo() {
|
|
1301
|
+
if (this.destroyed)
|
|
1302
|
+
return;
|
|
1303
|
+
this.viewer.scene.requestRender();
|
|
1304
|
+
}
|
|
1305
|
+
destroy() {
|
|
1306
|
+
if (this.destroyed)
|
|
1307
|
+
return;
|
|
1308
|
+
this.destroyed = true;
|
|
1309
|
+
this.stopRenderLoop();
|
|
1310
|
+
if (this.stage) {
|
|
1311
|
+
this.viewer.scene.postProcessStages.remove(this.stage);
|
|
1312
|
+
this.stage = null;
|
|
1313
|
+
}
|
|
1314
|
+
this.viewer.scene.requestRender();
|
|
1315
|
+
}
|
|
1316
|
+
isVisible() {
|
|
1317
|
+
return this.options.visible;
|
|
1318
|
+
}
|
|
1319
|
+
isDestroyed() {
|
|
1320
|
+
return this.destroyed;
|
|
1321
|
+
}
|
|
1322
|
+
getOptions() {
|
|
1323
|
+
return { ...this.options };
|
|
1324
|
+
}
|
|
1325
|
+
renderStage() {
|
|
1326
|
+
this.stage = this.viewer.scene.postProcessStages.add(new PostProcessStage({
|
|
1327
|
+
name: 'geo-effect-kit-scene-weather',
|
|
1328
|
+
fragmentShader: buildSceneWeatherPostProcessSource(),
|
|
1329
|
+
uniforms: createSceneWeatherUniforms(this.options),
|
|
1330
|
+
}));
|
|
1331
|
+
this.applyStageOptions();
|
|
1332
|
+
}
|
|
1333
|
+
applyStageOptions() {
|
|
1334
|
+
if (!this.stage)
|
|
1335
|
+
return;
|
|
1336
|
+
this.stage.enabled = this.options.visible;
|
|
1337
|
+
Object.assign(this.stage.uniforms, createSceneWeatherUniforms(this.options));
|
|
1338
|
+
}
|
|
1339
|
+
startRenderLoop() {
|
|
1340
|
+
if (this.renderFrame || !this.options.visible || typeof window === 'undefined')
|
|
1341
|
+
return;
|
|
1342
|
+
const tick = () => {
|
|
1343
|
+
if (this.destroyed || !this.options.visible) {
|
|
1344
|
+
this.renderFrame = 0;
|
|
1345
|
+
return;
|
|
1346
|
+
}
|
|
1347
|
+
this.viewer.scene.requestRender();
|
|
1348
|
+
this.renderFrame = window.requestAnimationFrame(tick);
|
|
1349
|
+
};
|
|
1350
|
+
this.renderFrame = window.requestAnimationFrame(tick);
|
|
1351
|
+
}
|
|
1352
|
+
stopRenderLoop() {
|
|
1353
|
+
if (!this.renderFrame || typeof window === 'undefined') {
|
|
1354
|
+
this.renderFrame = 0;
|
|
1355
|
+
return;
|
|
1356
|
+
}
|
|
1357
|
+
window.cancelAnimationFrame(this.renderFrame);
|
|
1358
|
+
this.renderFrame = 0;
|
|
1359
|
+
}
|
|
1360
|
+
}
|
|
1361
|
+
export class PostProcessEffect {
|
|
1362
|
+
viewer;
|
|
1363
|
+
options;
|
|
1364
|
+
stage = null;
|
|
1365
|
+
destroyed = false;
|
|
1366
|
+
constructor(viewer, options = {}) {
|
|
1367
|
+
this.viewer = viewer;
|
|
1368
|
+
this.options = normalizePostProcessOptions(options);
|
|
1369
|
+
this.renderStage();
|
|
1370
|
+
this.viewer.scene.requestRender();
|
|
1371
|
+
}
|
|
1372
|
+
update(options) {
|
|
1373
|
+
if (this.destroyed)
|
|
1374
|
+
return;
|
|
1375
|
+
this.options = normalizePostProcessOptions({
|
|
1376
|
+
...this.options,
|
|
1377
|
+
...options,
|
|
1378
|
+
visible: options.visible ?? this.options.visible,
|
|
1379
|
+
});
|
|
1380
|
+
this.applyStageOptions();
|
|
1381
|
+
this.viewer.scene.requestRender();
|
|
1382
|
+
}
|
|
1383
|
+
show() {
|
|
1384
|
+
if (this.destroyed)
|
|
1385
|
+
return;
|
|
1386
|
+
this.options = { ...this.options, visible: true };
|
|
1387
|
+
this.applyStageOptions();
|
|
1388
|
+
this.viewer.scene.requestRender();
|
|
1389
|
+
}
|
|
1390
|
+
hide() {
|
|
1391
|
+
if (this.destroyed)
|
|
1392
|
+
return;
|
|
1393
|
+
this.options = { ...this.options, visible: false };
|
|
1394
|
+
this.applyStageOptions();
|
|
1395
|
+
this.viewer.scene.requestRender();
|
|
1396
|
+
}
|
|
1397
|
+
flyTo() {
|
|
1398
|
+
if (this.destroyed)
|
|
1399
|
+
return;
|
|
1400
|
+
this.viewer.scene.requestRender();
|
|
1401
|
+
}
|
|
1402
|
+
destroy() {
|
|
1403
|
+
if (this.destroyed)
|
|
1404
|
+
return;
|
|
1405
|
+
this.destroyed = true;
|
|
1406
|
+
if (this.stage) {
|
|
1407
|
+
this.viewer.scene.postProcessStages.remove(this.stage);
|
|
1408
|
+
this.stage = null;
|
|
1409
|
+
}
|
|
1410
|
+
this.viewer.scene.requestRender();
|
|
1411
|
+
}
|
|
1412
|
+
isVisible() {
|
|
1413
|
+
return this.options.visible;
|
|
1414
|
+
}
|
|
1415
|
+
isDestroyed() {
|
|
1416
|
+
return this.destroyed;
|
|
1417
|
+
}
|
|
1418
|
+
getOptions() {
|
|
1419
|
+
return { ...this.options };
|
|
1420
|
+
}
|
|
1421
|
+
renderStage() {
|
|
1422
|
+
this.stage = this.viewer.scene.postProcessStages.add(new PostProcessStage({
|
|
1423
|
+
name: 'geo-effect-kit-post-process',
|
|
1424
|
+
fragmentShader: buildPostProcessSource(),
|
|
1425
|
+
uniforms: createPostProcessUniforms(this.options),
|
|
1426
|
+
}));
|
|
1427
|
+
this.applyStageOptions();
|
|
1428
|
+
}
|
|
1429
|
+
applyStageOptions() {
|
|
1430
|
+
if (!this.stage)
|
|
1431
|
+
return;
|
|
1432
|
+
this.stage.enabled = this.options.visible;
|
|
1433
|
+
Object.assign(this.stage.uniforms, createPostProcessUniforms(this.options));
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
export class TemperatureFieldEffect {
|
|
1437
|
+
viewer;
|
|
1438
|
+
dataSource;
|
|
1439
|
+
options;
|
|
1440
|
+
primitive = null;
|
|
1441
|
+
destroyed = false;
|
|
1442
|
+
constructor(viewer, options) {
|
|
1443
|
+
this.viewer = viewer;
|
|
1444
|
+
this.options = normalizeTemperatureFieldOptions(options);
|
|
1445
|
+
this.dataSource = new CustomDataSource('geo-effect-kit-temperature-field');
|
|
1446
|
+
this.viewer.dataSources.add(this.dataSource);
|
|
1447
|
+
this.renderPrimitive();
|
|
1448
|
+
this.syncOutlines();
|
|
1449
|
+
this.viewer.scene.requestRender();
|
|
1450
|
+
}
|
|
1451
|
+
update(options) {
|
|
1452
|
+
if (this.destroyed)
|
|
1453
|
+
return;
|
|
1454
|
+
const nextOptions = {
|
|
1455
|
+
polygons: options.polygons ?? this.options.polygons,
|
|
1456
|
+
stops: options.stops ?? this.options.stops,
|
|
1457
|
+
samples: options.samples ?? this.options.samples,
|
|
1458
|
+
seed: options.seed ?? this.options.seed,
|
|
1459
|
+
opacity: options.opacity ?? this.options.opacity,
|
|
1460
|
+
noiseStrength: options.noiseStrength ?? this.options.noiseStrength,
|
|
1461
|
+
contourLines: options.contourLines ?? this.options.contourLines,
|
|
1462
|
+
contourStrength: options.contourStrength ?? this.options.contourStrength,
|
|
1463
|
+
outline: options.outline ?? this.options.outline,
|
|
1464
|
+
outlineColor: options.outlineColor ?? this.options.outlineColor,
|
|
1465
|
+
outlineWidth: options.outlineWidth ?? this.options.outlineWidth,
|
|
1466
|
+
visible: options.visible ?? this.options.visible,
|
|
1467
|
+
};
|
|
1468
|
+
const nextBounds = options.bounds ?? this.options.bounds;
|
|
1469
|
+
if (nextBounds)
|
|
1470
|
+
nextOptions.bounds = nextBounds;
|
|
1471
|
+
const next = normalizeTemperatureFieldOptions(nextOptions);
|
|
1472
|
+
const rebuildPrimitive = shouldRebuildTemperatureField(this.options, next);
|
|
1473
|
+
this.options = next;
|
|
1474
|
+
if (rebuildPrimitive) {
|
|
1475
|
+
this.renderPrimitive();
|
|
1476
|
+
this.syncOutlines();
|
|
1477
|
+
}
|
|
1478
|
+
else {
|
|
1479
|
+
this.applyPrimitiveOptions();
|
|
1480
|
+
this.syncOutlineVisibility();
|
|
1481
|
+
}
|
|
1482
|
+
this.viewer.scene.requestRender();
|
|
1483
|
+
}
|
|
1484
|
+
show() {
|
|
1485
|
+
if (this.destroyed)
|
|
1486
|
+
return;
|
|
1487
|
+
this.options = { ...this.options, visible: true };
|
|
1488
|
+
this.applyPrimitiveOptions();
|
|
1489
|
+
this.syncOutlineVisibility();
|
|
1490
|
+
this.viewer.scene.requestRender();
|
|
1491
|
+
}
|
|
1492
|
+
hide() {
|
|
1493
|
+
if (this.destroyed)
|
|
1494
|
+
return;
|
|
1495
|
+
this.options = { ...this.options, visible: false };
|
|
1496
|
+
this.applyPrimitiveOptions();
|
|
1497
|
+
this.syncOutlineVisibility();
|
|
1498
|
+
this.viewer.scene.requestRender();
|
|
1499
|
+
}
|
|
1500
|
+
flyTo(options = {}) {
|
|
1501
|
+
if (this.destroyed || !this.options.bounds)
|
|
1502
|
+
return;
|
|
1503
|
+
this.viewer.camera.flyTo({
|
|
1504
|
+
destination: Rectangle.fromDegrees(this.options.bounds.west, this.options.bounds.south, this.options.bounds.east, this.options.bounds.north),
|
|
1505
|
+
duration: options.duration ?? 1,
|
|
1506
|
+
});
|
|
1507
|
+
}
|
|
1508
|
+
destroy() {
|
|
1509
|
+
if (this.destroyed)
|
|
1510
|
+
return;
|
|
1511
|
+
this.destroyed = true;
|
|
1512
|
+
this.removePrimitive();
|
|
1513
|
+
this.dataSource.entities.removeAll();
|
|
1514
|
+
this.viewer.dataSources.remove(this.dataSource, true);
|
|
1515
|
+
this.viewer.scene.requestRender();
|
|
1516
|
+
}
|
|
1517
|
+
isVisible() {
|
|
1518
|
+
return this.options.visible;
|
|
1519
|
+
}
|
|
1520
|
+
isDestroyed() {
|
|
1521
|
+
return this.destroyed;
|
|
1522
|
+
}
|
|
1523
|
+
getOptions() {
|
|
1524
|
+
return cloneTemperatureFieldOptions(this.options);
|
|
1525
|
+
}
|
|
1526
|
+
renderPrimitive() {
|
|
1527
|
+
this.removePrimitive();
|
|
1528
|
+
registerTemperatureFieldMaterial(this.options);
|
|
1529
|
+
if (this.options.polygons.length === 0)
|
|
1530
|
+
return;
|
|
1531
|
+
const instances = this.options.polygons.map((polygon, index) => {
|
|
1532
|
+
const outerPositions = polygon.outer.map(([longitude, latitude]) => Cartesian3.fromDegrees(longitude, latitude));
|
|
1533
|
+
const holes = polygon.holes.map((hole) => new PolygonHierarchy(hole.map(([longitude, latitude]) => Cartesian3.fromDegrees(longitude, latitude))));
|
|
1534
|
+
return new GeometryInstance({
|
|
1535
|
+
id: `geo-temperature-field-${index}`,
|
|
1536
|
+
geometry: new PolygonGeometry({
|
|
1537
|
+
polygonHierarchy: new PolygonHierarchy(outerPositions, holes),
|
|
1538
|
+
vertexFormat: EllipsoidSurfaceAppearance.VERTEX_FORMAT,
|
|
1539
|
+
}),
|
|
1540
|
+
});
|
|
1541
|
+
});
|
|
1542
|
+
this.primitive = this.viewer.scene.primitives.add(new GroundPrimitive({
|
|
1543
|
+
geometryInstances: instances,
|
|
1544
|
+
appearance: new EllipsoidSurfaceAppearance({
|
|
1545
|
+
material: createTemperatureFieldMaterial(this.options),
|
|
1546
|
+
}),
|
|
1547
|
+
show: this.options.visible,
|
|
1548
|
+
}));
|
|
1549
|
+
}
|
|
1550
|
+
removePrimitive() {
|
|
1551
|
+
if (!this.primitive)
|
|
1552
|
+
return;
|
|
1553
|
+
this.viewer.scene.primitives.remove(this.primitive);
|
|
1554
|
+
this.primitive = null;
|
|
1555
|
+
}
|
|
1556
|
+
applyPrimitiveOptions() {
|
|
1557
|
+
if (!this.primitive)
|
|
1558
|
+
return;
|
|
1559
|
+
this.primitive.show = this.options.visible;
|
|
1560
|
+
this.applyMaterialOptions();
|
|
1561
|
+
}
|
|
1562
|
+
applyMaterialOptions() {
|
|
1563
|
+
const material = this.primitive?.appearance?.material;
|
|
1564
|
+
if (!material)
|
|
1565
|
+
return;
|
|
1566
|
+
Object.assign(material.uniforms, createTemperatureFieldUniforms(this.options));
|
|
1567
|
+
}
|
|
1568
|
+
syncOutlines() {
|
|
1569
|
+
clearEntities(this.dataSource);
|
|
1570
|
+
if (!this.options.outline)
|
|
1571
|
+
return;
|
|
1572
|
+
this.options.polygons.forEach((polygon, index) => {
|
|
1573
|
+
this.addOutline(`${index}-outer`, polygon.outer);
|
|
1574
|
+
polygon.holes.forEach((hole, holeIndex) => {
|
|
1575
|
+
this.addOutline(`${index}-hole-${holeIndex}`, hole);
|
|
1576
|
+
});
|
|
1577
|
+
});
|
|
1578
|
+
}
|
|
1579
|
+
addOutline(id, ring) {
|
|
1580
|
+
if (ring.length < 2)
|
|
1581
|
+
return;
|
|
1582
|
+
const closedRing = isTemperatureRingClosed(ring) ? ring : [...ring, ring[0]];
|
|
1583
|
+
this.dataSource.entities.add({
|
|
1584
|
+
id: `geo-temperature-field-outline-${id}`,
|
|
1585
|
+
show: this.options.visible && this.options.outline,
|
|
1586
|
+
polyline: {
|
|
1587
|
+
positions: closedRing.map(([longitude, latitude]) => Cartesian3.fromDegrees(longitude, latitude)),
|
|
1588
|
+
clampToGround: true,
|
|
1589
|
+
width: this.options.outlineWidth,
|
|
1590
|
+
material: new PolylineGlowMaterialProperty({
|
|
1591
|
+
glowPower: 0.18,
|
|
1592
|
+
color: Color.fromCssColorString(this.options.outlineColor).withAlpha(0.76),
|
|
1593
|
+
}),
|
|
1594
|
+
},
|
|
1595
|
+
});
|
|
1596
|
+
}
|
|
1597
|
+
syncOutlineVisibility() {
|
|
1598
|
+
this.dataSource.entities.values.forEach((entity) => {
|
|
1599
|
+
entity.show = this.options.visible && this.options.outline;
|
|
1600
|
+
});
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
export class FireBillboardEffect {
|
|
1604
|
+
viewer;
|
|
1605
|
+
dataSource;
|
|
1606
|
+
options;
|
|
1607
|
+
entities = [];
|
|
1608
|
+
frameSets = [];
|
|
1609
|
+
frameTimer = null;
|
|
1610
|
+
frameIndex = 0;
|
|
1611
|
+
loadToken = 0;
|
|
1612
|
+
destroyed = false;
|
|
1613
|
+
constructor(viewer, options) {
|
|
1614
|
+
this.viewer = viewer;
|
|
1615
|
+
this.options = normalizeFireBillboardOptions(options);
|
|
1616
|
+
this.dataSource = new CustomDataSource('geo-effect-kit-fire-billboard');
|
|
1617
|
+
this.dataSource.show = this.options.visible;
|
|
1618
|
+
this.viewer.dataSources.add(this.dataSource);
|
|
1619
|
+
this.renderEntities();
|
|
1620
|
+
this.loadFrames();
|
|
1621
|
+
this.viewer.scene.requestRender();
|
|
1622
|
+
}
|
|
1623
|
+
update(options) {
|
|
1624
|
+
if (this.destroyed)
|
|
1625
|
+
return;
|
|
1626
|
+
const next = normalizeFireBillboardOptions({
|
|
1627
|
+
...this.options,
|
|
1628
|
+
...options,
|
|
1629
|
+
points: options.points ?? this.options.points,
|
|
1630
|
+
clampToGround: options.clampToGround ?? this.options.clampToGround,
|
|
1631
|
+
visible: options.visible ?? this.options.visible,
|
|
1632
|
+
});
|
|
1633
|
+
const rebuildEntities = shouldRebuildFireBillboard(this.options, next);
|
|
1634
|
+
const intervalChanged = this.options.frameIntervalMs !== next.frameIntervalMs;
|
|
1635
|
+
this.options = next;
|
|
1636
|
+
if (rebuildEntities) {
|
|
1637
|
+
this.stopFrameTimer();
|
|
1638
|
+
this.renderEntities();
|
|
1639
|
+
this.loadFrames();
|
|
1640
|
+
}
|
|
1641
|
+
else {
|
|
1642
|
+
this.applyBillboardOptions();
|
|
1643
|
+
if (intervalChanged)
|
|
1644
|
+
this.restartFrameTimer();
|
|
1645
|
+
}
|
|
1646
|
+
this.dataSource.show = this.options.visible;
|
|
1647
|
+
if (!this.options.visible)
|
|
1648
|
+
this.stopFrameTimer();
|
|
1649
|
+
this.viewer.scene.requestRender();
|
|
1650
|
+
}
|
|
1651
|
+
show() {
|
|
1652
|
+
if (this.destroyed)
|
|
1653
|
+
return;
|
|
1654
|
+
this.options = { ...this.options, visible: true };
|
|
1655
|
+
this.dataSource.show = true;
|
|
1656
|
+
this.loadFrames();
|
|
1657
|
+
this.viewer.scene.requestRender();
|
|
1658
|
+
}
|
|
1659
|
+
hide() {
|
|
1660
|
+
if (this.destroyed)
|
|
1661
|
+
return;
|
|
1662
|
+
this.options = { ...this.options, visible: false };
|
|
1663
|
+
this.dataSource.show = false;
|
|
1664
|
+
this.stopFrameTimer();
|
|
1665
|
+
this.viewer.scene.requestRender();
|
|
1666
|
+
}
|
|
1667
|
+
flyTo(options = {}) {
|
|
1668
|
+
if (this.destroyed || this.options.points.length === 0)
|
|
1669
|
+
return;
|
|
1670
|
+
const { center, radius } = getPositionBounds(this.options.points, Math.max(1200, this.options.scale * 2400));
|
|
1671
|
+
this.viewer.camera.flyToBoundingSphere(new BoundingSphere(center, radius), {
|
|
1672
|
+
duration: options.duration ?? 1,
|
|
1673
|
+
offset: new HeadingPitchRange(0, options.pitch ?? -0.62, radius * (options.rangeMultiplier ?? 2.4)),
|
|
1674
|
+
});
|
|
1675
|
+
}
|
|
1676
|
+
destroy() {
|
|
1677
|
+
if (this.destroyed)
|
|
1678
|
+
return;
|
|
1679
|
+
this.destroyed = true;
|
|
1680
|
+
this.loadToken += 1;
|
|
1681
|
+
this.stopFrameTimer();
|
|
1682
|
+
this.dataSource.entities.removeAll();
|
|
1683
|
+
this.entities = [];
|
|
1684
|
+
this.viewer.dataSources.remove(this.dataSource, true);
|
|
1685
|
+
this.viewer.scene.requestRender();
|
|
1686
|
+
}
|
|
1687
|
+
isVisible() {
|
|
1688
|
+
return this.options.visible;
|
|
1689
|
+
}
|
|
1690
|
+
isDestroyed() {
|
|
1691
|
+
return this.destroyed;
|
|
1692
|
+
}
|
|
1693
|
+
getOptions() {
|
|
1694
|
+
return {
|
|
1695
|
+
...this.options,
|
|
1696
|
+
points: cloneFireBillboardPoints(this.options.points),
|
|
1697
|
+
};
|
|
1698
|
+
}
|
|
1699
|
+
renderEntities() {
|
|
1700
|
+
clearEntities(this.dataSource);
|
|
1701
|
+
this.frameSets = this.options.points.map((point) => [point.gif]);
|
|
1702
|
+
this.entities = this.options.points.map((point, index) => {
|
|
1703
|
+
const billboard = new BillboardGraphics({
|
|
1704
|
+
image: new ConstantProperty(point.gif),
|
|
1705
|
+
scale: new ConstantProperty(this.options.scale),
|
|
1706
|
+
horizontalOrigin: HorizontalOrigin.CENTER,
|
|
1707
|
+
verticalOrigin: VerticalOrigin.BOTTOM,
|
|
1708
|
+
heightReference: this.options.clampToGround ? HeightReference.CLAMP_TO_GROUND : HeightReference.NONE,
|
|
1709
|
+
disableDepthTestDistance: this.options.disableDepthTestDistance,
|
|
1710
|
+
});
|
|
1711
|
+
const entityOptions = {
|
|
1712
|
+
id: point.id ?? `geo-effect-kit-fire-billboard-${index}`,
|
|
1713
|
+
position: Cartesian3.fromDegrees(point.longitude, point.latitude, point.height ?? 0),
|
|
1714
|
+
billboard,
|
|
1715
|
+
};
|
|
1716
|
+
if (point.label) {
|
|
1717
|
+
Object.assign(entityOptions, {
|
|
1718
|
+
label: {
|
|
1719
|
+
text: point.label,
|
|
1720
|
+
font: '12px sans-serif',
|
|
1721
|
+
fillColor: Color.WHITE,
|
|
1722
|
+
outlineColor: Color.BLACK,
|
|
1723
|
+
outlineWidth: 3,
|
|
1724
|
+
pixelOffset: new Cartesian2(0, -Math.max(24, 48 * this.options.scale)),
|
|
1725
|
+
showBackground: false,
|
|
1726
|
+
disableDepthTestDistance: this.options.disableDepthTestDistance,
|
|
1727
|
+
},
|
|
1728
|
+
});
|
|
1729
|
+
}
|
|
1730
|
+
return this.dataSource.entities.add(entityOptions);
|
|
1731
|
+
});
|
|
1732
|
+
}
|
|
1733
|
+
applyBillboardOptions() {
|
|
1734
|
+
this.entities.forEach((entity, index) => {
|
|
1735
|
+
const point = this.options.points[index];
|
|
1736
|
+
if (!point || !entity.billboard)
|
|
1737
|
+
return;
|
|
1738
|
+
entity.position = new ConstantPositionProperty(Cartesian3.fromDegrees(point.longitude, point.latitude, point.height ?? 0));
|
|
1739
|
+
entity.billboard.scale = new ConstantProperty(this.options.scale);
|
|
1740
|
+
entity.billboard.heightReference = new ConstantProperty(this.options.clampToGround ? HeightReference.CLAMP_TO_GROUND : HeightReference.NONE);
|
|
1741
|
+
entity.billboard.disableDepthTestDistance = new ConstantProperty(this.options.disableDepthTestDistance);
|
|
1742
|
+
if (entity.label) {
|
|
1743
|
+
entity.label.text = new ConstantProperty(point.label ?? '');
|
|
1744
|
+
entity.label.pixelOffset = new ConstantProperty(new Cartesian2(0, -Math.max(24, 48 * this.options.scale)));
|
|
1745
|
+
entity.label.disableDepthTestDistance = new ConstantProperty(this.options.disableDepthTestDistance);
|
|
1746
|
+
}
|
|
1747
|
+
});
|
|
1748
|
+
}
|
|
1749
|
+
loadFrames() {
|
|
1750
|
+
if (!this.options.visible || this.options.points.length === 0)
|
|
1751
|
+
return;
|
|
1752
|
+
const token = ++this.loadToken;
|
|
1753
|
+
this.options.points.forEach((point, index) => {
|
|
1754
|
+
loadGifFrameImages(point.gif).then((frames) => {
|
|
1755
|
+
if (this.destroyed || token !== this.loadToken)
|
|
1756
|
+
return;
|
|
1757
|
+
const currentPoint = this.options.points[index];
|
|
1758
|
+
if (!currentPoint || currentPoint.gif !== point.gif)
|
|
1759
|
+
return;
|
|
1760
|
+
this.applyFrameSet(index, frames);
|
|
1761
|
+
}).catch(() => {
|
|
1762
|
+
if (this.destroyed || token !== this.loadToken)
|
|
1763
|
+
return;
|
|
1764
|
+
const currentPoint = this.options.points[index];
|
|
1765
|
+
if (!currentPoint || currentPoint.gif !== point.gif)
|
|
1766
|
+
return;
|
|
1767
|
+
this.applyFrameSet(index, [point.gif]);
|
|
1768
|
+
});
|
|
1769
|
+
});
|
|
1770
|
+
}
|
|
1771
|
+
applyFrameSet(index, frames) {
|
|
1772
|
+
const entity = this.entities[index];
|
|
1773
|
+
const point = this.options.points[index];
|
|
1774
|
+
if (!entity?.billboard || !point)
|
|
1775
|
+
return;
|
|
1776
|
+
this.frameSets[index] = frames.length > 0 ? frames : [point.gif];
|
|
1777
|
+
entity.billboard.image = new ConstantProperty(this.frameSets[index][0] ?? point.gif);
|
|
1778
|
+
this.restartFrameTimer();
|
|
1779
|
+
this.viewer.scene.requestRender();
|
|
1780
|
+
}
|
|
1781
|
+
restartFrameTimer(frameSets) {
|
|
1782
|
+
this.stopFrameTimer();
|
|
1783
|
+
if (!this.options.visible)
|
|
1784
|
+
return;
|
|
1785
|
+
const sets = frameSets ?? this.frameSets;
|
|
1786
|
+
if (!sets.some((frames) => frames.length > 1))
|
|
1787
|
+
return;
|
|
1788
|
+
this.frameTimer = setInterval(() => {
|
|
1789
|
+
if (this.destroyed || !this.options.visible) {
|
|
1790
|
+
this.stopFrameTimer();
|
|
1791
|
+
return;
|
|
1792
|
+
}
|
|
1793
|
+
this.frameIndex += 1;
|
|
1794
|
+
this.entities.forEach((entity, index) => {
|
|
1795
|
+
const frames = sets[index];
|
|
1796
|
+
if (!entity.billboard || !frames || frames.length === 0)
|
|
1797
|
+
return;
|
|
1798
|
+
entity.billboard.image = new ConstantProperty(frames[this.frameIndex % frames.length] ?? frames[0]);
|
|
1799
|
+
});
|
|
1800
|
+
this.viewer.scene.requestRender();
|
|
1801
|
+
}, this.options.frameIntervalMs);
|
|
1802
|
+
}
|
|
1803
|
+
stopFrameTimer() {
|
|
1804
|
+
if (!this.frameTimer)
|
|
1805
|
+
return;
|
|
1806
|
+
clearInterval(this.frameTimer);
|
|
1807
|
+
this.frameTimer = null;
|
|
1808
|
+
}
|
|
1809
|
+
}
|
|
1810
|
+
export class WaterSurfaceEffect {
|
|
1811
|
+
viewer;
|
|
1812
|
+
dataSource;
|
|
1813
|
+
options;
|
|
1814
|
+
waterEntity = null;
|
|
1815
|
+
outlineEntity = null;
|
|
1816
|
+
material = null;
|
|
1817
|
+
renderFrame = 0;
|
|
1818
|
+
destroyed = false;
|
|
1819
|
+
constructor(viewer, options) {
|
|
1820
|
+
this.viewer = viewer;
|
|
1821
|
+
this.options = normalizeWaterSurfaceOptions(options);
|
|
1822
|
+
this.dataSource = new CustomDataSource('geo-effect-kit-water-surface');
|
|
1823
|
+
this.dataSource.show = this.options.visible;
|
|
1824
|
+
this.viewer.dataSources.add(this.dataSource);
|
|
1825
|
+
this.renderEntities();
|
|
1826
|
+
this.startRenderLoop();
|
|
1827
|
+
this.viewer.scene.requestRender();
|
|
1828
|
+
}
|
|
1829
|
+
update(options) {
|
|
1830
|
+
if (this.destroyed)
|
|
1831
|
+
return;
|
|
1832
|
+
const next = normalizeWaterSurfaceOptions({
|
|
1833
|
+
...this.options,
|
|
1834
|
+
...options,
|
|
1835
|
+
polygon: options.polygon ?? this.options.polygon,
|
|
1836
|
+
visible: options.visible ?? this.options.visible,
|
|
1837
|
+
});
|
|
1838
|
+
const rebuildEntities = shouldRebuildWaterSurface(this.options, next);
|
|
1839
|
+
this.options = next;
|
|
1840
|
+
if (rebuildEntities) {
|
|
1841
|
+
this.renderEntities();
|
|
1842
|
+
}
|
|
1843
|
+
else {
|
|
1844
|
+
this.applyMaterialOptions();
|
|
1845
|
+
}
|
|
1846
|
+
this.dataSource.show = this.options.visible;
|
|
1847
|
+
if (this.options.visible)
|
|
1848
|
+
this.startRenderLoop();
|
|
1849
|
+
else
|
|
1850
|
+
this.stopRenderLoop();
|
|
1851
|
+
this.viewer.scene.requestRender();
|
|
1852
|
+
}
|
|
1853
|
+
show() {
|
|
1854
|
+
if (this.destroyed)
|
|
1855
|
+
return;
|
|
1856
|
+
this.options = { ...this.options, visible: true };
|
|
1857
|
+
this.dataSource.show = true;
|
|
1858
|
+
this.startRenderLoop();
|
|
1859
|
+
this.viewer.scene.requestRender();
|
|
1860
|
+
}
|
|
1861
|
+
hide() {
|
|
1862
|
+
if (this.destroyed)
|
|
1863
|
+
return;
|
|
1864
|
+
this.options = { ...this.options, visible: false };
|
|
1865
|
+
this.dataSource.show = false;
|
|
1866
|
+
this.stopRenderLoop();
|
|
1867
|
+
this.viewer.scene.requestRender();
|
|
1868
|
+
}
|
|
1869
|
+
flyTo(options = {}) {
|
|
1870
|
+
if (this.destroyed)
|
|
1871
|
+
return;
|
|
1872
|
+
const bounds = getPositionBounds(this.options.polygon, 1200);
|
|
1873
|
+
this.viewer.camera.flyToBoundingSphere(new BoundingSphere(bounds.center, bounds.radius), {
|
|
1874
|
+
duration: options.duration ?? 1,
|
|
1875
|
+
offset: new HeadingPitchRange(0, options.pitch ?? -0.72, bounds.radius * (options.rangeMultiplier ?? 2.8)),
|
|
1876
|
+
});
|
|
1877
|
+
}
|
|
1878
|
+
destroy() {
|
|
1879
|
+
if (this.destroyed)
|
|
1880
|
+
return;
|
|
1881
|
+
this.destroyed = true;
|
|
1882
|
+
this.stopRenderLoop();
|
|
1883
|
+
this.dataSource.entities.removeAll();
|
|
1884
|
+
this.waterEntity = null;
|
|
1885
|
+
this.outlineEntity = null;
|
|
1886
|
+
this.material = null;
|
|
1887
|
+
this.viewer.dataSources.remove(this.dataSource, true);
|
|
1888
|
+
this.viewer.scene.requestRender();
|
|
1889
|
+
}
|
|
1890
|
+
isVisible() {
|
|
1891
|
+
return this.options.visible;
|
|
1892
|
+
}
|
|
1893
|
+
isDestroyed() {
|
|
1894
|
+
return this.destroyed;
|
|
1895
|
+
}
|
|
1896
|
+
getOptions() {
|
|
1897
|
+
return {
|
|
1898
|
+
...this.options,
|
|
1899
|
+
polygon: clonePositions(this.options.polygon),
|
|
1900
|
+
};
|
|
1901
|
+
}
|
|
1902
|
+
renderEntities() {
|
|
1903
|
+
clearEntities(this.dataSource);
|
|
1904
|
+
this.material = createWaterSurfaceMaterialProperty(this.options);
|
|
1905
|
+
this.waterEntity = this.dataSource.entities.add({
|
|
1906
|
+
id: 'geo-effect-kit-water-surface-polygon',
|
|
1907
|
+
polygon: {
|
|
1908
|
+
hierarchy: new PolygonHierarchy(positionsToCartesiansAtHeight(this.options.polygon, this.options.height)),
|
|
1909
|
+
material: this.material,
|
|
1910
|
+
perPositionHeight: this.options.height > 0,
|
|
1911
|
+
outline: false,
|
|
1912
|
+
},
|
|
1913
|
+
});
|
|
1914
|
+
this.syncOutlineEntity();
|
|
1915
|
+
}
|
|
1916
|
+
applyMaterialOptions() {
|
|
1917
|
+
if (!this.material)
|
|
1918
|
+
return;
|
|
1919
|
+
Object.assign(this.material.uniforms, createWaterSurfaceUniforms(this.options));
|
|
1920
|
+
}
|
|
1921
|
+
syncOutlineEntity() {
|
|
1922
|
+
if (!this.options.outline)
|
|
1923
|
+
return;
|
|
1924
|
+
this.outlineEntity = this.dataSource.entities.add({
|
|
1925
|
+
id: 'geo-effect-kit-water-surface-outline',
|
|
1926
|
+
polyline: {
|
|
1927
|
+
positions: positionsToCartesiansAtHeight(this.options.polygon, this.options.height + 1),
|
|
1928
|
+
clampToGround: this.options.height <= 0,
|
|
1929
|
+
width: 3,
|
|
1930
|
+
material: new PolylineGlowMaterialProperty({
|
|
1931
|
+
glowPower: 0.2,
|
|
1932
|
+
color: Color.fromCssColorString(this.options.color).withAlpha(0.72),
|
|
1933
|
+
}),
|
|
1934
|
+
},
|
|
1935
|
+
});
|
|
1936
|
+
}
|
|
1937
|
+
startRenderLoop() {
|
|
1938
|
+
if (this.renderFrame || !this.options.visible || typeof window === 'undefined')
|
|
1939
|
+
return;
|
|
1940
|
+
const tick = () => {
|
|
1941
|
+
if (this.destroyed || !this.options.visible) {
|
|
1942
|
+
this.renderFrame = 0;
|
|
1943
|
+
return;
|
|
1944
|
+
}
|
|
1945
|
+
this.viewer.scene.requestRender();
|
|
1946
|
+
this.renderFrame = window.requestAnimationFrame(tick);
|
|
1947
|
+
};
|
|
1948
|
+
this.renderFrame = window.requestAnimationFrame(tick);
|
|
1949
|
+
}
|
|
1950
|
+
stopRenderLoop() {
|
|
1951
|
+
if (!this.renderFrame || typeof window === 'undefined') {
|
|
1952
|
+
this.renderFrame = 0;
|
|
1953
|
+
return;
|
|
1954
|
+
}
|
|
1955
|
+
window.cancelAnimationFrame(this.renderFrame);
|
|
1956
|
+
this.renderFrame = 0;
|
|
1957
|
+
}
|
|
1958
|
+
}
|
|
1959
|
+
export class PolylineFlowEffect {
|
|
1960
|
+
viewer;
|
|
1961
|
+
dataSource;
|
|
1962
|
+
options;
|
|
1963
|
+
routeEntity = null;
|
|
1964
|
+
trailEntities = [];
|
|
1965
|
+
renderFrame = 0;
|
|
1966
|
+
destroyed = false;
|
|
1967
|
+
constructor(viewer, options) {
|
|
1968
|
+
this.viewer = viewer;
|
|
1969
|
+
this.options = normalizePolylineFlowOptions(options);
|
|
1970
|
+
this.dataSource = new CustomDataSource('geo-effect-kit-polyline-flow');
|
|
1971
|
+
this.dataSource.show = this.options.visible;
|
|
1972
|
+
this.viewer.dataSources.add(this.dataSource);
|
|
1973
|
+
this.renderEntities();
|
|
1974
|
+
this.startRenderLoop();
|
|
1975
|
+
this.viewer.scene.requestRender();
|
|
1976
|
+
}
|
|
1977
|
+
update(options) {
|
|
1978
|
+
if (this.destroyed)
|
|
1979
|
+
return;
|
|
1980
|
+
const next = normalizePolylineFlowOptions({
|
|
1981
|
+
...this.options,
|
|
1982
|
+
...options,
|
|
1983
|
+
positions: options.positions ?? this.options.positions,
|
|
1984
|
+
clampToGround: options.clampToGround ?? this.options.clampToGround,
|
|
1985
|
+
visible: options.visible ?? this.options.visible,
|
|
1986
|
+
});
|
|
1987
|
+
const rebuildEntities = shouldRebuildPolylineFlow(this.options, next);
|
|
1988
|
+
const pulseCountChanged = this.options.pulseCount !== next.pulseCount;
|
|
1989
|
+
this.options = next;
|
|
1990
|
+
if (rebuildEntities) {
|
|
1991
|
+
this.renderEntities();
|
|
1992
|
+
}
|
|
1993
|
+
else {
|
|
1994
|
+
this.applyRouteOptions();
|
|
1995
|
+
if (pulseCountChanged)
|
|
1996
|
+
this.syncTrailEntities();
|
|
1997
|
+
else
|
|
1998
|
+
this.applyTrailOptions();
|
|
1999
|
+
}
|
|
2000
|
+
this.dataSource.show = this.options.visible;
|
|
2001
|
+
if (this.options.visible)
|
|
2002
|
+
this.startRenderLoop();
|
|
2003
|
+
else
|
|
2004
|
+
this.stopRenderLoop();
|
|
2005
|
+
this.viewer.scene.requestRender();
|
|
2006
|
+
}
|
|
2007
|
+
show() {
|
|
2008
|
+
if (this.destroyed)
|
|
2009
|
+
return;
|
|
2010
|
+
this.options = { ...this.options, visible: true };
|
|
2011
|
+
this.dataSource.show = true;
|
|
2012
|
+
this.startRenderLoop();
|
|
2013
|
+
this.viewer.scene.requestRender();
|
|
2014
|
+
}
|
|
2015
|
+
hide() {
|
|
2016
|
+
if (this.destroyed)
|
|
2017
|
+
return;
|
|
2018
|
+
this.options = { ...this.options, visible: false };
|
|
2019
|
+
this.dataSource.show = false;
|
|
2020
|
+
this.stopRenderLoop();
|
|
2021
|
+
this.viewer.scene.requestRender();
|
|
2022
|
+
}
|
|
2023
|
+
flyTo(options = {}) {
|
|
2024
|
+
if (this.destroyed)
|
|
2025
|
+
return;
|
|
2026
|
+
const { center, radius } = getPositionBounds(this.getRenderPositions(), Math.max(2000, this.options.width * 1000));
|
|
2027
|
+
this.viewer.camera.flyToBoundingSphere(new BoundingSphere(center, radius), {
|
|
2028
|
+
duration: options.duration ?? 1,
|
|
2029
|
+
offset: new HeadingPitchRange(0, options.pitch ?? -0.62, radius * (options.rangeMultiplier ?? 2.4)),
|
|
2030
|
+
});
|
|
2031
|
+
}
|
|
2032
|
+
destroy() {
|
|
2033
|
+
if (this.destroyed)
|
|
2034
|
+
return;
|
|
2035
|
+
this.destroyed = true;
|
|
2036
|
+
this.stopRenderLoop();
|
|
2037
|
+
this.dataSource.entities.removeAll();
|
|
2038
|
+
this.routeEntity = null;
|
|
2039
|
+
this.trailEntities = [];
|
|
2040
|
+
this.viewer.dataSources.remove(this.dataSource, true);
|
|
2041
|
+
this.viewer.scene.requestRender();
|
|
2042
|
+
}
|
|
2043
|
+
isVisible() {
|
|
2044
|
+
return this.options.visible;
|
|
2045
|
+
}
|
|
2046
|
+
isDestroyed() {
|
|
2047
|
+
return this.destroyed;
|
|
2048
|
+
}
|
|
2049
|
+
getOptions() {
|
|
2050
|
+
return {
|
|
2051
|
+
...this.options,
|
|
2052
|
+
positions: clonePositions(this.options.positions),
|
|
2053
|
+
};
|
|
2054
|
+
}
|
|
2055
|
+
renderEntities() {
|
|
2056
|
+
clearEntities(this.dataSource);
|
|
2057
|
+
this.trailEntities = [];
|
|
2058
|
+
this.routeEntity = this.dataSource.entities.add({
|
|
2059
|
+
id: 'geo-effect-kit-polyline-flow-route',
|
|
2060
|
+
polyline: {
|
|
2061
|
+
positions: positionsToCartesians(this.getRenderPositions()),
|
|
2062
|
+
width: this.options.width,
|
|
2063
|
+
clampToGround: this.options.clampToGround,
|
|
2064
|
+
material: this.createBaseMaterial(),
|
|
2065
|
+
},
|
|
2066
|
+
});
|
|
2067
|
+
this.syncTrailEntities();
|
|
2068
|
+
}
|
|
2069
|
+
syncTrailEntities() {
|
|
2070
|
+
this.trailEntities.forEach((entity) => this.dataSource.entities.remove(entity));
|
|
2071
|
+
this.trailEntities = [];
|
|
2072
|
+
for (let index = 0; index < this.options.pulseCount; index += 1) {
|
|
2073
|
+
const trailIndex = index;
|
|
2074
|
+
const entity = this.dataSource.entities.add({
|
|
2075
|
+
id: `geo-effect-kit-polyline-flow-trail-${index}`,
|
|
2076
|
+
polyline: {
|
|
2077
|
+
positions: new CallbackProperty(() => this.getTrailCartesians(trailIndex), false),
|
|
2078
|
+
width: Math.max(1, this.options.width * (1.24 - index * 0.035)),
|
|
2079
|
+
clampToGround: this.options.clampToGround,
|
|
2080
|
+
material: this.createTrailMaterial(index),
|
|
2081
|
+
},
|
|
2082
|
+
});
|
|
2083
|
+
this.trailEntities.push(entity);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
applyRouteOptions() {
|
|
2087
|
+
if (!this.routeEntity?.polyline)
|
|
2088
|
+
return;
|
|
2089
|
+
this.routeEntity.polyline.width = new ConstantProperty(this.options.width);
|
|
2090
|
+
this.routeEntity.polyline.material = this.createBaseMaterial();
|
|
2091
|
+
}
|
|
2092
|
+
applyTrailOptions() {
|
|
2093
|
+
this.trailEntities.forEach((entity, index) => {
|
|
2094
|
+
if (!entity.polyline)
|
|
2095
|
+
return;
|
|
2096
|
+
entity.polyline.width = new ConstantProperty(Math.max(1, this.options.width * (1.24 - index * 0.035)));
|
|
2097
|
+
entity.polyline.material = this.createTrailMaterial(index);
|
|
2098
|
+
});
|
|
2099
|
+
}
|
|
2100
|
+
createBaseMaterial() {
|
|
2101
|
+
return new PolylineGlowMaterialProperty({
|
|
2102
|
+
color: Color.fromCssColorString(this.options.color).withAlpha(0.58),
|
|
2103
|
+
glowPower: this.options.glowPower,
|
|
2104
|
+
taperPower: this.options.taperPower,
|
|
2105
|
+
});
|
|
2106
|
+
}
|
|
2107
|
+
createTrailMaterial(index) {
|
|
2108
|
+
const alpha = clamp(0.92 - index * 0.045, 0.35, 0.94);
|
|
2109
|
+
return new PolylineGlowMaterialProperty({
|
|
2110
|
+
color: getPolylineFlowColor(this.options.type, this.options.color).withAlpha(alpha),
|
|
2111
|
+
glowPower: clamp(this.options.glowPower + 0.18, 0, 1),
|
|
2112
|
+
taperPower: clamp(this.options.taperPower - 0.18, 0, 1),
|
|
2113
|
+
});
|
|
2114
|
+
}
|
|
2115
|
+
getTrailCartesians(index) {
|
|
2116
|
+
const phase = fract(getAnimationSeconds() * this.options.speed * 0.22 + index / Math.max(1, this.options.pulseCount));
|
|
2117
|
+
const trail = samplePolylineTrail(this.getRenderPositions(), phase, this.options.trailLength, getTrailSampleCount(this.options.type));
|
|
2118
|
+
return positionsToCartesians(trail);
|
|
2119
|
+
}
|
|
2120
|
+
getRenderPositions() {
|
|
2121
|
+
return roundPolylineCorners(this.options.positions, this.options.cornerRadius);
|
|
2122
|
+
}
|
|
2123
|
+
startRenderLoop() {
|
|
2124
|
+
if (this.renderFrame || !this.options.visible || typeof window === 'undefined')
|
|
2125
|
+
return;
|
|
2126
|
+
const tick = () => {
|
|
2127
|
+
if (this.destroyed || !this.options.visible) {
|
|
2128
|
+
this.renderFrame = 0;
|
|
2129
|
+
return;
|
|
2130
|
+
}
|
|
2131
|
+
this.viewer.scene.requestRender();
|
|
2132
|
+
this.renderFrame = window.requestAnimationFrame(tick);
|
|
2133
|
+
};
|
|
2134
|
+
this.renderFrame = window.requestAnimationFrame(tick);
|
|
2135
|
+
}
|
|
2136
|
+
stopRenderLoop() {
|
|
2137
|
+
if (!this.renderFrame || typeof window === 'undefined') {
|
|
2138
|
+
this.renderFrame = 0;
|
|
2139
|
+
return;
|
|
2140
|
+
}
|
|
2141
|
+
window.cancelAnimationFrame(this.renderFrame);
|
|
2142
|
+
this.renderFrame = 0;
|
|
2143
|
+
}
|
|
2144
|
+
}
|
|
2145
|
+
export class FlyLineEffect {
|
|
2146
|
+
viewer;
|
|
2147
|
+
dataSource;
|
|
2148
|
+
options;
|
|
2149
|
+
baseEntities = [];
|
|
2150
|
+
trailEntities = [];
|
|
2151
|
+
endpointEntities = [];
|
|
2152
|
+
renderFrame = 0;
|
|
2153
|
+
destroyed = false;
|
|
2154
|
+
constructor(viewer, options) {
|
|
2155
|
+
this.viewer = viewer;
|
|
2156
|
+
this.options = normalizeFlyLineOptions(options);
|
|
2157
|
+
this.dataSource = new CustomDataSource('geo-effect-kit-fly-line');
|
|
2158
|
+
this.dataSource.show = this.options.visible;
|
|
2159
|
+
this.viewer.dataSources.add(this.dataSource);
|
|
2160
|
+
this.renderEntities();
|
|
2161
|
+
this.startRenderLoop();
|
|
2162
|
+
this.viewer.scene.requestRender();
|
|
2163
|
+
}
|
|
2164
|
+
update(options) {
|
|
2165
|
+
if (this.destroyed)
|
|
2166
|
+
return;
|
|
2167
|
+
const next = normalizeFlyLineOptions({
|
|
2168
|
+
...this.options,
|
|
2169
|
+
...options,
|
|
2170
|
+
lines: options.lines ?? this.options.lines,
|
|
2171
|
+
showEndpoints: options.showEndpoints ?? this.options.showEndpoints,
|
|
2172
|
+
visible: options.visible ?? this.options.visible,
|
|
2173
|
+
});
|
|
2174
|
+
const rebuildEntities = shouldRebuildFlyLine(this.options, next);
|
|
2175
|
+
const pulseCountChanged = this.options.pulseCount !== next.pulseCount;
|
|
2176
|
+
this.options = next;
|
|
2177
|
+
if (rebuildEntities) {
|
|
2178
|
+
this.renderEntities();
|
|
2179
|
+
}
|
|
2180
|
+
else {
|
|
2181
|
+
this.applyBaseOptions();
|
|
2182
|
+
if (pulseCountChanged)
|
|
2183
|
+
this.syncTrailEntities();
|
|
2184
|
+
else
|
|
2185
|
+
this.applyTrailOptions();
|
|
2186
|
+
this.syncEndpointEntities();
|
|
2187
|
+
}
|
|
2188
|
+
this.dataSource.show = this.options.visible;
|
|
2189
|
+
if (this.options.visible)
|
|
2190
|
+
this.startRenderLoop();
|
|
2191
|
+
else
|
|
2192
|
+
this.stopRenderLoop();
|
|
2193
|
+
this.viewer.scene.requestRender();
|
|
2194
|
+
}
|
|
2195
|
+
show() {
|
|
2196
|
+
if (this.destroyed)
|
|
2197
|
+
return;
|
|
2198
|
+
this.options = { ...this.options, visible: true };
|
|
2199
|
+
this.dataSource.show = true;
|
|
2200
|
+
this.startRenderLoop();
|
|
2201
|
+
this.viewer.scene.requestRender();
|
|
2202
|
+
}
|
|
2203
|
+
hide() {
|
|
2204
|
+
if (this.destroyed)
|
|
2205
|
+
return;
|
|
2206
|
+
this.options = { ...this.options, visible: false };
|
|
2207
|
+
this.dataSource.show = false;
|
|
2208
|
+
this.stopRenderLoop();
|
|
2209
|
+
this.viewer.scene.requestRender();
|
|
2210
|
+
}
|
|
2211
|
+
flyTo(options = {}) {
|
|
2212
|
+
if (this.destroyed)
|
|
2213
|
+
return;
|
|
2214
|
+
const positions = expandFlyLineRoutes(this.options.lines, this.options.mode).flatMap((line) => sampleFlyLineArc(line, this.options.arcHeight, 12));
|
|
2215
|
+
const { center, radius } = getPositionBounds(positions, Math.max(5000, this.options.arcHeight));
|
|
2216
|
+
this.viewer.camera.flyToBoundingSphere(new BoundingSphere(center, radius), {
|
|
2217
|
+
duration: options.duration ?? 1,
|
|
2218
|
+
offset: new HeadingPitchRange(0, options.pitch ?? -0.64, radius * (options.rangeMultiplier ?? 2.45)),
|
|
2219
|
+
});
|
|
2220
|
+
}
|
|
2221
|
+
destroy() {
|
|
2222
|
+
if (this.destroyed)
|
|
2223
|
+
return;
|
|
2224
|
+
this.destroyed = true;
|
|
2225
|
+
this.stopRenderLoop();
|
|
2226
|
+
this.dataSource.entities.removeAll();
|
|
2227
|
+
this.baseEntities = [];
|
|
2228
|
+
this.trailEntities = [];
|
|
2229
|
+
this.endpointEntities = [];
|
|
2230
|
+
this.viewer.dataSources.remove(this.dataSource, true);
|
|
2231
|
+
this.viewer.scene.requestRender();
|
|
2232
|
+
}
|
|
2233
|
+
isVisible() {
|
|
2234
|
+
return this.options.visible;
|
|
2235
|
+
}
|
|
2236
|
+
isDestroyed() {
|
|
2237
|
+
return this.destroyed;
|
|
2238
|
+
}
|
|
2239
|
+
getOptions() {
|
|
2240
|
+
return {
|
|
2241
|
+
...this.options,
|
|
2242
|
+
lines: cloneFlyLineRoutes(this.options.lines),
|
|
2243
|
+
};
|
|
2244
|
+
}
|
|
2245
|
+
renderEntities() {
|
|
2246
|
+
clearEntities(this.dataSource);
|
|
2247
|
+
this.baseEntities = [];
|
|
2248
|
+
this.trailEntities = [];
|
|
2249
|
+
this.endpointEntities = [];
|
|
2250
|
+
const routes = expandFlyLineRoutes(this.options.lines, this.options.mode);
|
|
2251
|
+
routes.forEach((line, index) => {
|
|
2252
|
+
const routeIndex = index;
|
|
2253
|
+
const entity = this.dataSource.entities.add({
|
|
2254
|
+
id: `geo-effect-kit-fly-line-base-${index}`,
|
|
2255
|
+
polyline: {
|
|
2256
|
+
positions: positionsToCartesians(sampleFlyLineArc(line, this.options.arcHeight, getFlyLineBaseSampleCount(this.options.mode))),
|
|
2257
|
+
width: this.options.width,
|
|
2258
|
+
material: this.createBaseMaterial(),
|
|
2259
|
+
},
|
|
2260
|
+
});
|
|
2261
|
+
this.baseEntities.push(entity);
|
|
2262
|
+
for (let pulseIndex = 0; pulseIndex < this.options.pulseCount; pulseIndex += 1) {
|
|
2263
|
+
this.addTrailEntity(routeIndex, pulseIndex);
|
|
2264
|
+
}
|
|
2265
|
+
});
|
|
2266
|
+
this.syncEndpointEntities();
|
|
2267
|
+
}
|
|
2268
|
+
syncTrailEntities() {
|
|
2269
|
+
this.trailEntities.forEach((entity) => this.dataSource.entities.remove(entity));
|
|
2270
|
+
this.trailEntities = [];
|
|
2271
|
+
expandFlyLineRoutes(this.options.lines, this.options.mode).forEach((_line, routeIndex) => {
|
|
2272
|
+
for (let pulseIndex = 0; pulseIndex < this.options.pulseCount; pulseIndex += 1) {
|
|
2273
|
+
this.addTrailEntity(routeIndex, pulseIndex);
|
|
2274
|
+
}
|
|
2275
|
+
});
|
|
2276
|
+
}
|
|
2277
|
+
addTrailEntity(routeIndex, pulseIndex) {
|
|
2278
|
+
const entity = this.dataSource.entities.add({
|
|
2279
|
+
id: `geo-effect-kit-fly-line-trail-${routeIndex}-${pulseIndex}`,
|
|
2280
|
+
polyline: {
|
|
2281
|
+
positions: new CallbackProperty(() => this.getTrailCartesians(routeIndex, pulseIndex), false),
|
|
2282
|
+
width: Math.max(1, this.options.width * (1.32 - pulseIndex * 0.04)),
|
|
2283
|
+
material: this.createTrailMaterial(pulseIndex),
|
|
2284
|
+
},
|
|
2285
|
+
});
|
|
2286
|
+
this.trailEntities.push(entity);
|
|
2287
|
+
}
|
|
2288
|
+
syncEndpointEntities() {
|
|
2289
|
+
this.endpointEntities.forEach((entity) => this.dataSource.entities.remove(entity));
|
|
2290
|
+
this.endpointEntities = [];
|
|
2291
|
+
if (!this.options.showEndpoints)
|
|
2292
|
+
return;
|
|
2293
|
+
const endpoints = uniqueFlyLineEndpoints(this.options.lines);
|
|
2294
|
+
endpoints.forEach((position, index) => {
|
|
2295
|
+
const entity = this.dataSource.entities.add({
|
|
2296
|
+
id: `geo-effect-kit-fly-line-endpoint-${index}`,
|
|
2297
|
+
position: Cartesian3.fromDegrees(position.longitude, position.latitude, position.height ?? 0),
|
|
2298
|
+
point: new PointGraphics({
|
|
2299
|
+
pixelSize: 9,
|
|
2300
|
+
color: new ConstantProperty(Color.fromCssColorString(this.options.color).withAlpha(0.92)),
|
|
2301
|
+
outlineColor: new ConstantProperty(Color.WHITE.withAlpha(0.68)),
|
|
2302
|
+
outlineWidth: 2,
|
|
2303
|
+
heightReference: HeightReference.NONE,
|
|
2304
|
+
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
|
2305
|
+
}),
|
|
2306
|
+
});
|
|
2307
|
+
this.endpointEntities.push(entity);
|
|
2308
|
+
});
|
|
2309
|
+
}
|
|
2310
|
+
applyBaseOptions() {
|
|
2311
|
+
this.baseEntities.forEach((entity) => {
|
|
2312
|
+
if (!entity.polyline)
|
|
2313
|
+
return;
|
|
2314
|
+
entity.polyline.width = new ConstantProperty(this.options.width);
|
|
2315
|
+
entity.polyline.material = this.createBaseMaterial();
|
|
2316
|
+
});
|
|
2317
|
+
}
|
|
2318
|
+
applyTrailOptions() {
|
|
2319
|
+
this.trailEntities.forEach((entity, index) => {
|
|
2320
|
+
if (!entity.polyline)
|
|
2321
|
+
return;
|
|
2322
|
+
const pulseIndex = index % Math.max(1, this.options.pulseCount);
|
|
2323
|
+
entity.polyline.width = new ConstantProperty(Math.max(1, this.options.width * (1.32 - pulseIndex * 0.04)));
|
|
2324
|
+
entity.polyline.material = this.createTrailMaterial(pulseIndex);
|
|
2325
|
+
});
|
|
2326
|
+
}
|
|
2327
|
+
createBaseMaterial() {
|
|
2328
|
+
return new PolylineGlowMaterialProperty({
|
|
2329
|
+
color: getFlyLineBaseColor(this.options.mode, this.options.color).withAlpha(0.4),
|
|
2330
|
+
glowPower: this.options.glowPower,
|
|
2331
|
+
taperPower: this.options.taperPower,
|
|
2332
|
+
});
|
|
2333
|
+
}
|
|
2334
|
+
createTrailMaterial(pulseIndex) {
|
|
2335
|
+
return new PolylineGlowMaterialProperty({
|
|
2336
|
+
color: getFlyLineTrailColor(this.options.mode, this.options.color).withAlpha(clamp(0.94 - pulseIndex * 0.04, 0.42, 0.94)),
|
|
2337
|
+
glowPower: clamp(this.options.glowPower + 0.2, 0, 1),
|
|
2338
|
+
taperPower: clamp(this.options.taperPower - 0.22, 0, 1),
|
|
2339
|
+
});
|
|
2340
|
+
}
|
|
2341
|
+
getTrailCartesians(routeIndex, pulseIndex) {
|
|
2342
|
+
const routes = expandFlyLineRoutes(this.options.lines, this.options.mode);
|
|
2343
|
+
const route = routes[routeIndex] ?? routes[0];
|
|
2344
|
+
if (!route)
|
|
2345
|
+
return [];
|
|
2346
|
+
const phase = fract(getAnimationSeconds() * this.options.speed * getFlyLineSpeedScale(this.options.mode) +
|
|
2347
|
+
pulseIndex / Math.max(1, this.options.pulseCount));
|
|
2348
|
+
const trail = sampleFlyLineTrail(route, this.options.arcHeight, phase, this.options.trailLength, getFlyLineTrailSampleCount(this.options.mode));
|
|
2349
|
+
return positionsToCartesians(trail);
|
|
2350
|
+
}
|
|
2351
|
+
startRenderLoop() {
|
|
2352
|
+
if (this.renderFrame || !this.options.visible || typeof window === 'undefined')
|
|
2353
|
+
return;
|
|
2354
|
+
const tick = () => {
|
|
2355
|
+
if (this.destroyed || !this.options.visible) {
|
|
2356
|
+
this.renderFrame = 0;
|
|
2357
|
+
return;
|
|
2358
|
+
}
|
|
2359
|
+
this.viewer.scene.requestRender();
|
|
2360
|
+
this.renderFrame = window.requestAnimationFrame(tick);
|
|
2361
|
+
};
|
|
2362
|
+
this.renderFrame = window.requestAnimationFrame(tick);
|
|
2363
|
+
}
|
|
2364
|
+
stopRenderLoop() {
|
|
2365
|
+
if (!this.renderFrame || typeof window === 'undefined') {
|
|
2366
|
+
this.renderFrame = 0;
|
|
2367
|
+
return;
|
|
2368
|
+
}
|
|
2369
|
+
window.cancelAnimationFrame(this.renderFrame);
|
|
2370
|
+
this.renderFrame = 0;
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
export class PipeFlowEffect {
|
|
2374
|
+
viewer;
|
|
2375
|
+
dataSource;
|
|
2376
|
+
options;
|
|
2377
|
+
pipeShellEntity = null;
|
|
2378
|
+
pipeHighlightEntity = null;
|
|
2379
|
+
waterCoreEntity = null;
|
|
2380
|
+
waveEntities = [];
|
|
2381
|
+
bubbleEntities = [];
|
|
2382
|
+
renderFrame = 0;
|
|
2383
|
+
destroyed = false;
|
|
2384
|
+
constructor(viewer, options) {
|
|
2385
|
+
this.viewer = viewer;
|
|
2386
|
+
this.options = normalizePipeFlowOptions(options);
|
|
2387
|
+
this.dataSource = new CustomDataSource('geo-effect-kit-pipe-flow');
|
|
2388
|
+
this.dataSource.show = this.options.visible;
|
|
2389
|
+
this.viewer.dataSources.add(this.dataSource);
|
|
2390
|
+
this.renderEntities();
|
|
2391
|
+
this.startRenderLoop();
|
|
2392
|
+
this.viewer.scene.requestRender();
|
|
2393
|
+
}
|
|
2394
|
+
update(options) {
|
|
2395
|
+
if (this.destroyed)
|
|
2396
|
+
return;
|
|
2397
|
+
const next = normalizePipeFlowOptions({
|
|
2398
|
+
...this.options,
|
|
2399
|
+
...options,
|
|
2400
|
+
positions: options.positions ?? this.options.positions,
|
|
2401
|
+
clampToGround: options.clampToGround ?? this.options.clampToGround,
|
|
2402
|
+
visible: options.visible ?? this.options.visible,
|
|
2403
|
+
});
|
|
2404
|
+
const rebuildEntities = shouldRebuildPipeFlow(this.options, next);
|
|
2405
|
+
this.options = next;
|
|
2406
|
+
if (rebuildEntities)
|
|
2407
|
+
this.renderEntities();
|
|
2408
|
+
else
|
|
2409
|
+
this.applyOptions();
|
|
2410
|
+
this.dataSource.show = this.options.visible;
|
|
2411
|
+
if (this.options.visible)
|
|
2412
|
+
this.startRenderLoop();
|
|
2413
|
+
else
|
|
2414
|
+
this.stopRenderLoop();
|
|
2415
|
+
this.viewer.scene.requestRender();
|
|
2416
|
+
}
|
|
2417
|
+
show() {
|
|
2418
|
+
if (this.destroyed)
|
|
2419
|
+
return;
|
|
2420
|
+
this.options = { ...this.options, visible: true };
|
|
2421
|
+
this.dataSource.show = true;
|
|
2422
|
+
this.startRenderLoop();
|
|
2423
|
+
this.viewer.scene.requestRender();
|
|
2424
|
+
}
|
|
2425
|
+
hide() {
|
|
2426
|
+
if (this.destroyed)
|
|
2427
|
+
return;
|
|
2428
|
+
this.options = { ...this.options, visible: false };
|
|
2429
|
+
this.dataSource.show = false;
|
|
2430
|
+
this.stopRenderLoop();
|
|
2431
|
+
this.viewer.scene.requestRender();
|
|
2432
|
+
}
|
|
2433
|
+
flyTo(options = {}) {
|
|
2434
|
+
if (this.destroyed)
|
|
2435
|
+
return;
|
|
2436
|
+
const { center, radius } = getPositionBounds(this.getRenderPositions(), Math.max(2400, this.options.width * 1000));
|
|
2437
|
+
this.viewer.camera.flyToBoundingSphere(new BoundingSphere(center, radius), {
|
|
2438
|
+
duration: options.duration ?? 1,
|
|
2439
|
+
offset: new HeadingPitchRange(0, options.pitch ?? -0.62, radius * (options.rangeMultiplier ?? 2.35)),
|
|
2440
|
+
});
|
|
2441
|
+
}
|
|
2442
|
+
destroy() {
|
|
2443
|
+
if (this.destroyed)
|
|
2444
|
+
return;
|
|
2445
|
+
this.destroyed = true;
|
|
2446
|
+
this.stopRenderLoop();
|
|
2447
|
+
this.dataSource.entities.removeAll();
|
|
2448
|
+
this.pipeShellEntity = null;
|
|
2449
|
+
this.pipeHighlightEntity = null;
|
|
2450
|
+
this.waterCoreEntity = null;
|
|
2451
|
+
this.waveEntities = [];
|
|
2452
|
+
this.bubbleEntities = [];
|
|
2453
|
+
this.viewer.dataSources.remove(this.dataSource, true);
|
|
2454
|
+
this.viewer.scene.requestRender();
|
|
2455
|
+
}
|
|
2456
|
+
isVisible() {
|
|
2457
|
+
return this.options.visible;
|
|
2458
|
+
}
|
|
2459
|
+
isDestroyed() {
|
|
2460
|
+
return this.destroyed;
|
|
2461
|
+
}
|
|
2462
|
+
getOptions() {
|
|
2463
|
+
return {
|
|
2464
|
+
...this.options,
|
|
2465
|
+
positions: clonePositions(this.options.positions),
|
|
2466
|
+
};
|
|
2467
|
+
}
|
|
2468
|
+
renderEntities() {
|
|
2469
|
+
clearEntities(this.dataSource);
|
|
2470
|
+
this.waveEntities = [];
|
|
2471
|
+
this.bubbleEntities = [];
|
|
2472
|
+
const positions = this.getRenderCartesians();
|
|
2473
|
+
this.pipeShellEntity = this.dataSource.entities.add({
|
|
2474
|
+
id: 'geo-effect-kit-pipe-flow-shell',
|
|
2475
|
+
polyline: {
|
|
2476
|
+
positions,
|
|
2477
|
+
width: getPipeLayerWidth(this.options.width, 2),
|
|
2478
|
+
clampToGround: this.options.clampToGround,
|
|
2479
|
+
material: this.createPipeShellMaterial(),
|
|
2480
|
+
},
|
|
2481
|
+
});
|
|
2482
|
+
this.pipeHighlightEntity = this.dataSource.entities.add({
|
|
2483
|
+
id: 'geo-effect-kit-pipe-flow-highlight',
|
|
2484
|
+
polyline: {
|
|
2485
|
+
positions,
|
|
2486
|
+
width: getPipeLayerWidth(this.options.width, 1.2),
|
|
2487
|
+
clampToGround: this.options.clampToGround,
|
|
2488
|
+
material: this.createPipeHighlightMaterial(),
|
|
2489
|
+
},
|
|
2490
|
+
});
|
|
2491
|
+
this.waterCoreEntity = this.dataSource.entities.add({
|
|
2492
|
+
id: 'geo-effect-kit-pipe-flow-water-core',
|
|
2493
|
+
polyline: {
|
|
2494
|
+
positions,
|
|
2495
|
+
width: getPipeLayerWidth(this.options.width, 0.8),
|
|
2496
|
+
clampToGround: this.options.clampToGround,
|
|
2497
|
+
material: this.createWaterCoreMaterial(),
|
|
2498
|
+
},
|
|
2499
|
+
});
|
|
2500
|
+
for (let index = 0; index < 2; index += 1) {
|
|
2501
|
+
const waveIndex = index;
|
|
2502
|
+
const entity = this.dataSource.entities.add({
|
|
2503
|
+
id: `geo-effect-kit-pipe-flow-pressure-wave-${index}`,
|
|
2504
|
+
polyline: {
|
|
2505
|
+
positions: new CallbackProperty(() => this.getMovingSegmentCartesians(waveIndex, 0.18), false),
|
|
2506
|
+
width: getPipeLayerWidth(this.options.width, 0.92 - index * 0.12),
|
|
2507
|
+
clampToGround: this.options.clampToGround,
|
|
2508
|
+
material: this.createPressureWaveMaterial(index),
|
|
2509
|
+
},
|
|
2510
|
+
});
|
|
2511
|
+
this.waveEntities.push(entity);
|
|
2512
|
+
}
|
|
2513
|
+
for (let index = 0; index < this.options.bubbleDensity; index += 1) {
|
|
2514
|
+
const bubbleIndex = index;
|
|
2515
|
+
const entity = this.dataSource.entities.add({
|
|
2516
|
+
id: `geo-effect-kit-pipe-flow-bubble-${index}`,
|
|
2517
|
+
polyline: {
|
|
2518
|
+
positions: new CallbackProperty(() => this.getMovingSegmentCartesians(bubbleIndex, 0.055), false),
|
|
2519
|
+
width: getPipeLayerWidth(this.options.width, 0.24),
|
|
2520
|
+
clampToGround: this.options.clampToGround,
|
|
2521
|
+
material: this.createBubbleMaterial(index),
|
|
2522
|
+
},
|
|
2523
|
+
});
|
|
2524
|
+
this.bubbleEntities.push(entity);
|
|
2525
|
+
}
|
|
2526
|
+
}
|
|
2527
|
+
applyOptions() {
|
|
2528
|
+
if (this.pipeShellEntity?.polyline) {
|
|
2529
|
+
this.pipeShellEntity.polyline.width = new ConstantProperty(getPipeLayerWidth(this.options.width, 2));
|
|
2530
|
+
this.pipeShellEntity.polyline.material = this.createPipeShellMaterial();
|
|
2531
|
+
}
|
|
2532
|
+
if (this.pipeHighlightEntity?.polyline) {
|
|
2533
|
+
this.pipeHighlightEntity.polyline.width = new ConstantProperty(getPipeLayerWidth(this.options.width, 1.2));
|
|
2534
|
+
this.pipeHighlightEntity.polyline.material = this.createPipeHighlightMaterial();
|
|
2535
|
+
}
|
|
2536
|
+
if (this.waterCoreEntity?.polyline) {
|
|
2537
|
+
this.waterCoreEntity.polyline.width = new ConstantProperty(getPipeLayerWidth(this.options.width, 0.8));
|
|
2538
|
+
this.waterCoreEntity.polyline.material = this.createWaterCoreMaterial();
|
|
2539
|
+
}
|
|
2540
|
+
this.waveEntities.forEach((entity, index) => {
|
|
2541
|
+
if (!entity.polyline)
|
|
2542
|
+
return;
|
|
2543
|
+
entity.polyline.width = new ConstantProperty(getPipeLayerWidth(this.options.width, 0.92 - index * 0.12));
|
|
2544
|
+
entity.polyline.material = this.createPressureWaveMaterial(index);
|
|
2545
|
+
});
|
|
2546
|
+
this.bubbleEntities.forEach((entity, index) => {
|
|
2547
|
+
if (!entity.polyline)
|
|
2548
|
+
return;
|
|
2549
|
+
entity.polyline.width = new ConstantProperty(getPipeLayerWidth(this.options.width, 0.24));
|
|
2550
|
+
entity.polyline.material = this.createBubbleMaterial(index);
|
|
2551
|
+
});
|
|
2552
|
+
}
|
|
2553
|
+
createPipeShellMaterial() {
|
|
2554
|
+
return new PolylineGlowMaterialProperty({
|
|
2555
|
+
color: Color.fromCssColorString(this.options.color).withAlpha(this.options.pipeOpacity),
|
|
2556
|
+
glowPower: 0.18,
|
|
2557
|
+
taperPower: 0.82,
|
|
2558
|
+
});
|
|
2559
|
+
}
|
|
2560
|
+
createPipeHighlightMaterial() {
|
|
2561
|
+
return new PolylineGlowMaterialProperty({
|
|
2562
|
+
color: Color.WHITE.withAlpha(clamp(this.options.pipeOpacity * 0.55, 0.08, 0.38)),
|
|
2563
|
+
glowPower: 0.08,
|
|
2564
|
+
taperPower: 1,
|
|
2565
|
+
});
|
|
2566
|
+
}
|
|
2567
|
+
createWaterCoreMaterial() {
|
|
2568
|
+
return new PolylineGlowMaterialProperty({
|
|
2569
|
+
color: Color.fromCssColorString(this.options.color).withAlpha(this.options.waterOpacity),
|
|
2570
|
+
glowPower: 0.28,
|
|
2571
|
+
taperPower: 0.44,
|
|
2572
|
+
});
|
|
2573
|
+
}
|
|
2574
|
+
createPressureWaveMaterial(index) {
|
|
2575
|
+
return new PolylineGlowMaterialProperty({
|
|
2576
|
+
color: Color.lerp(Color.fromCssColorString(this.options.color), Color.WHITE, 0.35, new Color()).withAlpha(clamp(this.options.waterOpacity - index * 0.18, 0.22, 0.9)),
|
|
2577
|
+
glowPower: 0.36,
|
|
2578
|
+
taperPower: 0.2,
|
|
2579
|
+
});
|
|
2580
|
+
}
|
|
2581
|
+
createBubbleMaterial(index) {
|
|
2582
|
+
return new PolylineGlowMaterialProperty({
|
|
2583
|
+
color: Color.lerp(Color.fromCssColorString(this.options.color), Color.WHITE, 0.72, new Color()).withAlpha(clamp(0.72 - (index % 4) * 0.08, 0.36, 0.72)),
|
|
2584
|
+
glowPower: 0.18,
|
|
2585
|
+
taperPower: 0,
|
|
2586
|
+
});
|
|
2587
|
+
}
|
|
2588
|
+
getMovingSegmentCartesians(index, length) {
|
|
2589
|
+
const density = Math.max(1, this.options.bubbleDensity);
|
|
2590
|
+
const phase = fract(getAnimationSeconds() * this.options.speed * 0.32 + index / density);
|
|
2591
|
+
const segment = samplePolylineTrail(this.getRenderPositions(), phase, length, 4);
|
|
2592
|
+
return positionsToCartesians(segment);
|
|
2593
|
+
}
|
|
2594
|
+
getRenderCartesians() {
|
|
2595
|
+
return positionsToCartesians(this.getRenderPositions());
|
|
2596
|
+
}
|
|
2597
|
+
getRenderPositions() {
|
|
2598
|
+
return roundPolylineCorners(this.options.positions, this.options.cornerRadius);
|
|
2599
|
+
}
|
|
2600
|
+
startRenderLoop() {
|
|
2601
|
+
if (this.renderFrame || !this.options.visible || typeof window === 'undefined')
|
|
2602
|
+
return;
|
|
2603
|
+
const tick = () => {
|
|
2604
|
+
if (this.destroyed || !this.options.visible) {
|
|
2605
|
+
this.renderFrame = 0;
|
|
2606
|
+
return;
|
|
2607
|
+
}
|
|
2608
|
+
this.viewer.scene.requestRender();
|
|
2609
|
+
this.renderFrame = window.requestAnimationFrame(tick);
|
|
2610
|
+
};
|
|
2611
|
+
this.renderFrame = window.requestAnimationFrame(tick);
|
|
2612
|
+
}
|
|
2613
|
+
stopRenderLoop() {
|
|
2614
|
+
if (!this.renderFrame || typeof window === 'undefined') {
|
|
2615
|
+
this.renderFrame = 0;
|
|
2616
|
+
return;
|
|
2617
|
+
}
|
|
2618
|
+
window.cancelAnimationFrame(this.renderFrame);
|
|
2619
|
+
this.renderFrame = 0;
|
|
2620
|
+
}
|
|
2621
|
+
}
|
|
2622
|
+
export class LightWallEffect {
|
|
2623
|
+
viewer;
|
|
2624
|
+
dataSource;
|
|
2625
|
+
options;
|
|
2626
|
+
wallEntity = null;
|
|
2627
|
+
outlineEntity = null;
|
|
2628
|
+
material = null;
|
|
2629
|
+
renderFrame = 0;
|
|
2630
|
+
destroyed = false;
|
|
2631
|
+
constructor(viewer, options) {
|
|
2632
|
+
this.viewer = viewer;
|
|
2633
|
+
this.options = normalizeLightWallOptions(options);
|
|
2634
|
+
this.dataSource = new CustomDataSource('geo-effect-kit-light-wall');
|
|
2635
|
+
this.dataSource.show = this.options.visible;
|
|
2636
|
+
this.viewer.dataSources.add(this.dataSource);
|
|
2637
|
+
this.renderEntities();
|
|
2638
|
+
this.startRenderLoop();
|
|
2639
|
+
this.viewer.scene.requestRender();
|
|
2640
|
+
}
|
|
2641
|
+
update(options) {
|
|
2642
|
+
if (this.destroyed)
|
|
2643
|
+
return;
|
|
2644
|
+
const next = normalizeLightWallOptions({
|
|
2645
|
+
...this.options,
|
|
2646
|
+
...options,
|
|
2647
|
+
positions: options.positions ?? this.options.positions,
|
|
2648
|
+
breathing: options.breathing ?? this.options.breathing,
|
|
2649
|
+
outline: options.outline ?? this.options.outline,
|
|
2650
|
+
visible: options.visible ?? this.options.visible,
|
|
2651
|
+
});
|
|
2652
|
+
const rebuildEntities = shouldRebuildLightWall(this.options, next);
|
|
2653
|
+
this.options = next;
|
|
2654
|
+
if (rebuildEntities) {
|
|
2655
|
+
this.renderEntities();
|
|
2656
|
+
}
|
|
2657
|
+
else {
|
|
2658
|
+
this.applyMaterialOptions();
|
|
2659
|
+
this.syncOutlineEntity();
|
|
2660
|
+
}
|
|
2661
|
+
this.dataSource.show = this.options.visible;
|
|
2662
|
+
if (this.options.visible)
|
|
2663
|
+
this.startRenderLoop();
|
|
2664
|
+
else
|
|
2665
|
+
this.stopRenderLoop();
|
|
2666
|
+
this.viewer.scene.requestRender();
|
|
2667
|
+
}
|
|
2668
|
+
show() {
|
|
2669
|
+
if (this.destroyed)
|
|
2670
|
+
return;
|
|
2671
|
+
this.options = { ...this.options, visible: true };
|
|
2672
|
+
this.dataSource.show = true;
|
|
2673
|
+
this.startRenderLoop();
|
|
2674
|
+
this.viewer.scene.requestRender();
|
|
2675
|
+
}
|
|
2676
|
+
hide() {
|
|
2677
|
+
if (this.destroyed)
|
|
2678
|
+
return;
|
|
2679
|
+
this.options = { ...this.options, visible: false };
|
|
2680
|
+
this.dataSource.show = false;
|
|
2681
|
+
this.stopRenderLoop();
|
|
2682
|
+
this.viewer.scene.requestRender();
|
|
2683
|
+
}
|
|
2684
|
+
flyTo(options = {}) {
|
|
2685
|
+
if (this.destroyed)
|
|
2686
|
+
return;
|
|
2687
|
+
const { center, radius } = getPositionBounds(this.options.positions, this.options.height);
|
|
2688
|
+
this.viewer.camera.flyToBoundingSphere(new BoundingSphere(center, radius), {
|
|
2689
|
+
duration: options.duration ?? 1,
|
|
2690
|
+
offset: new HeadingPitchRange(0, options.pitch ?? -0.58, radius * (options.rangeMultiplier ?? 2.35)),
|
|
2691
|
+
});
|
|
2692
|
+
}
|
|
2693
|
+
destroy() {
|
|
2694
|
+
if (this.destroyed)
|
|
2695
|
+
return;
|
|
2696
|
+
this.destroyed = true;
|
|
2697
|
+
this.stopRenderLoop();
|
|
2698
|
+
this.dataSource.entities.removeAll();
|
|
2699
|
+
this.wallEntity = null;
|
|
2700
|
+
this.outlineEntity = null;
|
|
2701
|
+
this.material = null;
|
|
2702
|
+
this.viewer.dataSources.remove(this.dataSource, true);
|
|
2703
|
+
this.viewer.scene.requestRender();
|
|
2704
|
+
}
|
|
2705
|
+
isVisible() {
|
|
2706
|
+
return this.options.visible;
|
|
2707
|
+
}
|
|
2708
|
+
isDestroyed() {
|
|
2709
|
+
return this.destroyed;
|
|
2710
|
+
}
|
|
2711
|
+
getOptions() {
|
|
2712
|
+
return {
|
|
2713
|
+
...this.options,
|
|
2714
|
+
positions: clonePositions(this.options.positions),
|
|
2715
|
+
};
|
|
2716
|
+
}
|
|
2717
|
+
renderEntities() {
|
|
2718
|
+
clearEntities(this.dataSource);
|
|
2719
|
+
this.outlineEntity = null;
|
|
2720
|
+
this.material = createLightWallMaterialProperty(this.options);
|
|
2721
|
+
this.wallEntity = this.dataSource.entities.add({
|
|
2722
|
+
id: 'geo-effect-kit-light-wall-surface',
|
|
2723
|
+
wall: {
|
|
2724
|
+
positions: positionsToCartesians(this.options.positions),
|
|
2725
|
+
minimumHeights: this.options.positions.map((position) => position.height ?? 0),
|
|
2726
|
+
maximumHeights: this.options.positions.map((position) => (position.height ?? 0) + this.options.height),
|
|
2727
|
+
material: this.material,
|
|
2728
|
+
outline: false,
|
|
2729
|
+
},
|
|
2730
|
+
});
|
|
2731
|
+
this.syncOutlineEntity();
|
|
2732
|
+
}
|
|
2733
|
+
applyMaterialOptions() {
|
|
2734
|
+
if (!this.material)
|
|
2735
|
+
return;
|
|
2736
|
+
this.material.uniforms.color = Color.fromCssColorString(this.options.color).withAlpha(1);
|
|
2737
|
+
this.material.uniforms.opacity = this.options.opacity;
|
|
2738
|
+
this.material.uniforms.speed = this.options.speed;
|
|
2739
|
+
this.material.uniforms.wallType = getLightWallTypeUniform(this.options.type);
|
|
2740
|
+
this.material.uniforms.scanLineCount = this.options.scanLineCount;
|
|
2741
|
+
this.material.uniforms.breathing = this.options.breathing ? 1 : 0;
|
|
2742
|
+
}
|
|
2743
|
+
syncOutlineEntity() {
|
|
2744
|
+
if (!this.options.outline) {
|
|
2745
|
+
if (this.outlineEntity) {
|
|
2746
|
+
this.dataSource.entities.remove(this.outlineEntity);
|
|
2747
|
+
this.outlineEntity = null;
|
|
2748
|
+
}
|
|
2749
|
+
return;
|
|
2750
|
+
}
|
|
2751
|
+
if (this.outlineEntity) {
|
|
2752
|
+
if (this.outlineEntity.polyline) {
|
|
2753
|
+
this.outlineEntity.polyline.positions = new ConstantProperty(positionsToCartesiansAtHeight(this.options.positions, this.options.height));
|
|
2754
|
+
this.outlineEntity.polyline.material = new PolylineGlowMaterialProperty({
|
|
2755
|
+
color: Color.fromCssColorString(this.options.color).withAlpha(0.86),
|
|
2756
|
+
glowPower: 0.18,
|
|
2757
|
+
taperPower: 1,
|
|
2758
|
+
});
|
|
2759
|
+
}
|
|
2760
|
+
return;
|
|
2761
|
+
}
|
|
2762
|
+
this.outlineEntity = this.dataSource.entities.add({
|
|
2763
|
+
id: 'geo-effect-kit-light-wall-outline',
|
|
2764
|
+
polyline: {
|
|
2765
|
+
positions: positionsToCartesiansAtHeight(this.options.positions, this.options.height),
|
|
2766
|
+
width: 2.5,
|
|
2767
|
+
material: new PolylineGlowMaterialProperty({
|
|
2768
|
+
color: Color.fromCssColorString(this.options.color).withAlpha(0.86),
|
|
2769
|
+
glowPower: 0.18,
|
|
2770
|
+
taperPower: 1,
|
|
2771
|
+
}),
|
|
2772
|
+
},
|
|
2773
|
+
});
|
|
2774
|
+
}
|
|
2775
|
+
startRenderLoop() {
|
|
2776
|
+
if (this.renderFrame || !this.options.visible || typeof window === 'undefined')
|
|
2777
|
+
return;
|
|
2778
|
+
const tick = () => {
|
|
2779
|
+
if (this.destroyed || !this.options.visible) {
|
|
2780
|
+
this.renderFrame = 0;
|
|
2781
|
+
return;
|
|
2782
|
+
}
|
|
2783
|
+
this.viewer.scene.requestRender();
|
|
2784
|
+
this.renderFrame = window.requestAnimationFrame(tick);
|
|
2785
|
+
};
|
|
2786
|
+
this.renderFrame = window.requestAnimationFrame(tick);
|
|
2787
|
+
}
|
|
2788
|
+
stopRenderLoop() {
|
|
2789
|
+
if (!this.renderFrame || typeof window === 'undefined') {
|
|
2790
|
+
this.renderFrame = 0;
|
|
2791
|
+
return;
|
|
2792
|
+
}
|
|
2793
|
+
window.cancelAnimationFrame(this.renderFrame);
|
|
2794
|
+
this.renderFrame = 0;
|
|
2795
|
+
}
|
|
2796
|
+
}
|
|
2797
|
+
export class ScanConeEffect {
|
|
2798
|
+
viewer;
|
|
2799
|
+
dataSource;
|
|
2800
|
+
options;
|
|
2801
|
+
coneEntity = null;
|
|
2802
|
+
originEntity = null;
|
|
2803
|
+
material = null;
|
|
2804
|
+
renderFrame = 0;
|
|
2805
|
+
destroyed = false;
|
|
2806
|
+
constructor(viewer, options) {
|
|
2807
|
+
this.viewer = viewer;
|
|
2808
|
+
this.options = normalizeScanConeOptions(options);
|
|
2809
|
+
this.dataSource = new CustomDataSource('geo-effect-kit-scan-cone');
|
|
2810
|
+
this.dataSource.show = this.options.visible;
|
|
2811
|
+
this.viewer.dataSources.add(this.dataSource);
|
|
2812
|
+
this.renderEntities();
|
|
2813
|
+
this.startRenderLoop();
|
|
2814
|
+
this.viewer.scene.requestRender();
|
|
2815
|
+
}
|
|
2816
|
+
update(options) {
|
|
2817
|
+
if (this.destroyed)
|
|
2818
|
+
return;
|
|
2819
|
+
const next = normalizeScanConeOptions({
|
|
2820
|
+
...this.options,
|
|
2821
|
+
...options,
|
|
2822
|
+
center: options.center ?? this.options.center,
|
|
2823
|
+
showOrigin: options.showOrigin ?? this.options.showOrigin,
|
|
2824
|
+
visible: options.visible ?? this.options.visible,
|
|
2825
|
+
});
|
|
2826
|
+
const rebuildEntities = shouldRebuildScanCone(this.options, next);
|
|
2827
|
+
this.options = next;
|
|
2828
|
+
if (rebuildEntities) {
|
|
2829
|
+
this.renderEntities();
|
|
2830
|
+
}
|
|
2831
|
+
else {
|
|
2832
|
+
this.applyMaterialOptions();
|
|
2833
|
+
this.syncOriginEntity();
|
|
2834
|
+
}
|
|
2835
|
+
this.dataSource.show = this.options.visible;
|
|
2836
|
+
if (this.options.visible)
|
|
2837
|
+
this.startRenderLoop();
|
|
2838
|
+
else
|
|
2839
|
+
this.stopRenderLoop();
|
|
2840
|
+
this.viewer.scene.requestRender();
|
|
2841
|
+
}
|
|
2842
|
+
show() {
|
|
2843
|
+
if (this.destroyed)
|
|
2844
|
+
return;
|
|
2845
|
+
this.options = { ...this.options, visible: true };
|
|
2846
|
+
this.dataSource.show = true;
|
|
2847
|
+
this.startRenderLoop();
|
|
2848
|
+
this.viewer.scene.requestRender();
|
|
2849
|
+
}
|
|
2850
|
+
hide() {
|
|
2851
|
+
if (this.destroyed)
|
|
2852
|
+
return;
|
|
2853
|
+
this.options = { ...this.options, visible: false };
|
|
2854
|
+
this.dataSource.show = false;
|
|
2855
|
+
this.stopRenderLoop();
|
|
2856
|
+
this.viewer.scene.requestRender();
|
|
2857
|
+
}
|
|
2858
|
+
flyTo(options = {}) {
|
|
2859
|
+
if (this.destroyed)
|
|
2860
|
+
return;
|
|
2861
|
+
const center = this.getOriginCartesian();
|
|
2862
|
+
const radius = Math.max(this.options.radiusMeters, this.options.lengthMeters);
|
|
2863
|
+
this.viewer.camera.flyToBoundingSphere(new BoundingSphere(center, radius), {
|
|
2864
|
+
duration: options.duration ?? 1,
|
|
2865
|
+
offset: new HeadingPitchRange(0, options.pitch ?? -0.46, radius * (options.rangeMultiplier ?? 2.8)),
|
|
2866
|
+
});
|
|
2867
|
+
}
|
|
2868
|
+
destroy() {
|
|
2869
|
+
if (this.destroyed)
|
|
2870
|
+
return;
|
|
2871
|
+
this.destroyed = true;
|
|
2872
|
+
this.stopRenderLoop();
|
|
2873
|
+
this.dataSource.entities.removeAll();
|
|
2874
|
+
this.coneEntity = null;
|
|
2875
|
+
this.originEntity = null;
|
|
2876
|
+
this.material = null;
|
|
2877
|
+
this.viewer.dataSources.remove(this.dataSource, true);
|
|
2878
|
+
this.viewer.scene.requestRender();
|
|
2879
|
+
}
|
|
2880
|
+
isVisible() {
|
|
2881
|
+
return this.options.visible;
|
|
2882
|
+
}
|
|
2883
|
+
isDestroyed() {
|
|
2884
|
+
return this.destroyed;
|
|
2885
|
+
}
|
|
2886
|
+
getOptions() {
|
|
2887
|
+
return {
|
|
2888
|
+
...this.options,
|
|
2889
|
+
center: { ...this.options.center },
|
|
2890
|
+
};
|
|
2891
|
+
}
|
|
2892
|
+
renderEntities() {
|
|
2893
|
+
clearEntities(this.dataSource);
|
|
2894
|
+
this.originEntity = null;
|
|
2895
|
+
this.material = createScanConeMaterialProperty(this.options);
|
|
2896
|
+
this.coneEntity = this.dataSource.entities.add({
|
|
2897
|
+
id: 'geo-effect-kit-scan-cone-volume',
|
|
2898
|
+
position: new CallbackPositionProperty(() => this.getConeCenterCartesian(), false),
|
|
2899
|
+
orientation: new CallbackProperty(() => this.getConeOrientation(), false),
|
|
2900
|
+
cylinder: {
|
|
2901
|
+
length: this.options.lengthMeters,
|
|
2902
|
+
topRadius: 0,
|
|
2903
|
+
bottomRadius: getConeBottomRadius(this.options),
|
|
2904
|
+
slices: 128,
|
|
2905
|
+
numberOfVerticalLines: 24,
|
|
2906
|
+
material: this.material,
|
|
2907
|
+
outline: false,
|
|
2908
|
+
},
|
|
2909
|
+
});
|
|
2910
|
+
this.syncOriginEntity();
|
|
2911
|
+
}
|
|
2912
|
+
applyMaterialOptions() {
|
|
2913
|
+
if (!this.material)
|
|
2914
|
+
return;
|
|
2915
|
+
this.material.uniforms.color = Color.fromCssColorString(this.options.color).withAlpha(1);
|
|
2916
|
+
this.material.uniforms.opacity = this.options.opacity;
|
|
2917
|
+
this.material.uniforms.speed = this.options.speed;
|
|
2918
|
+
this.material.uniforms.coneType = getScanConeTypeUniform(this.options.type);
|
|
2919
|
+
this.material.uniforms.aperture = this.options.aperture;
|
|
2920
|
+
}
|
|
2921
|
+
syncOriginEntity() {
|
|
2922
|
+
if (!this.options.showOrigin) {
|
|
2923
|
+
if (this.originEntity) {
|
|
2924
|
+
this.dataSource.entities.remove(this.originEntity);
|
|
2925
|
+
this.originEntity = null;
|
|
2926
|
+
}
|
|
2927
|
+
return;
|
|
2928
|
+
}
|
|
2929
|
+
const color = Color.fromCssColorString(this.options.color);
|
|
2930
|
+
if (!this.originEntity) {
|
|
2931
|
+
this.originEntity = this.dataSource.entities.add({
|
|
2932
|
+
id: 'geo-effect-kit-scan-cone-origin',
|
|
2933
|
+
position: this.getOriginCartesian(),
|
|
2934
|
+
point: new PointGraphics({
|
|
2935
|
+
pixelSize: 11,
|
|
2936
|
+
color: new ConstantProperty(color),
|
|
2937
|
+
outlineColor: new ConstantProperty(Color.WHITE.withAlpha(0.72)),
|
|
2938
|
+
outlineWidth: 2,
|
|
2939
|
+
heightReference: HeightReference.CLAMP_TO_GROUND,
|
|
2940
|
+
disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
|
2941
|
+
}),
|
|
2942
|
+
});
|
|
2943
|
+
return;
|
|
2944
|
+
}
|
|
2945
|
+
this.originEntity.position = new ConstantPositionProperty(this.getOriginCartesian());
|
|
2946
|
+
if (this.originEntity.point)
|
|
2947
|
+
this.originEntity.point.color = new ConstantProperty(color);
|
|
2948
|
+
}
|
|
2949
|
+
getOriginCartesian() {
|
|
2950
|
+
return Cartesian3.fromDegrees(this.options.center.longitude, this.options.center.latitude, this.options.center.height ?? 0);
|
|
2951
|
+
}
|
|
2952
|
+
getConeCenterCartesian() {
|
|
2953
|
+
return Cartesian3.fromDegrees(this.options.center.longitude, this.options.center.latitude, (this.options.center.height ?? 0) + this.options.lengthMeters / 2);
|
|
2954
|
+
}
|
|
2955
|
+
getConeOrientation() {
|
|
2956
|
+
const heading = this.options.heading + getAnimationSeconds() * this.options.speed * 36;
|
|
2957
|
+
const hpr = HeadingPitchRoll.fromDegrees(heading, this.options.pitch, 0);
|
|
2958
|
+
return Transforms.headingPitchRollQuaternion(this.getConeCenterCartesian(), hpr);
|
|
2959
|
+
}
|
|
2960
|
+
startRenderLoop() {
|
|
2961
|
+
if (this.renderFrame || !this.options.visible || typeof window === 'undefined')
|
|
2962
|
+
return;
|
|
2963
|
+
const tick = () => {
|
|
2964
|
+
if (this.destroyed || !this.options.visible) {
|
|
2965
|
+
this.renderFrame = 0;
|
|
2966
|
+
return;
|
|
2967
|
+
}
|
|
2968
|
+
this.viewer.scene.requestRender();
|
|
2969
|
+
this.renderFrame = window.requestAnimationFrame(tick);
|
|
2970
|
+
};
|
|
2971
|
+
this.renderFrame = window.requestAnimationFrame(tick);
|
|
2972
|
+
}
|
|
2973
|
+
stopRenderLoop() {
|
|
2974
|
+
if (!this.renderFrame || typeof window === 'undefined') {
|
|
2975
|
+
this.renderFrame = 0;
|
|
2976
|
+
return;
|
|
2977
|
+
}
|
|
2978
|
+
window.cancelAnimationFrame(this.renderFrame);
|
|
2979
|
+
this.renderFrame = 0;
|
|
2980
|
+
}
|
|
2981
|
+
}
|
|
2982
|
+
export class ShieldDomeEffect {
|
|
2983
|
+
viewer;
|
|
2984
|
+
dataSource;
|
|
2985
|
+
options;
|
|
2986
|
+
domeEntity = null;
|
|
2987
|
+
ringEntity = null;
|
|
2988
|
+
material = null;
|
|
2989
|
+
renderFrame = 0;
|
|
2990
|
+
destroyed = false;
|
|
2991
|
+
constructor(viewer, options) {
|
|
2992
|
+
this.viewer = viewer;
|
|
2993
|
+
this.options = normalizeShieldDomeOptions(options);
|
|
2994
|
+
this.dataSource = new CustomDataSource('geo-effect-kit-shield-dome');
|
|
2995
|
+
this.dataSource.show = this.options.visible;
|
|
2996
|
+
this.viewer.dataSources.add(this.dataSource);
|
|
2997
|
+
this.renderEntities();
|
|
2998
|
+
this.startRenderLoop();
|
|
2999
|
+
this.viewer.scene.requestRender();
|
|
3000
|
+
}
|
|
3001
|
+
update(options) {
|
|
3002
|
+
if (this.destroyed)
|
|
3003
|
+
return;
|
|
3004
|
+
const next = normalizeShieldDomeOptions({
|
|
3005
|
+
...this.options,
|
|
3006
|
+
...options,
|
|
3007
|
+
center: options.center ?? this.options.center,
|
|
3008
|
+
radiusMeters: options.radiusMeters ?? this.options.radiusMeters,
|
|
3009
|
+
ring: options.ring ?? this.options.ring,
|
|
3010
|
+
visible: options.visible ?? this.options.visible,
|
|
3011
|
+
});
|
|
3012
|
+
const rebuildEntities = shouldRebuildShieldDome(this.options, next);
|
|
3013
|
+
this.options = next;
|
|
3014
|
+
if (rebuildEntities) {
|
|
3015
|
+
this.renderEntities();
|
|
3016
|
+
}
|
|
3017
|
+
else {
|
|
3018
|
+
this.applyMaterialOptions();
|
|
3019
|
+
this.syncRingEntity();
|
|
3020
|
+
}
|
|
3021
|
+
this.dataSource.show = this.options.visible;
|
|
3022
|
+
if (this.options.visible)
|
|
3023
|
+
this.startRenderLoop();
|
|
3024
|
+
else
|
|
3025
|
+
this.stopRenderLoop();
|
|
3026
|
+
this.viewer.scene.requestRender();
|
|
3027
|
+
}
|
|
3028
|
+
show() {
|
|
3029
|
+
if (this.destroyed)
|
|
3030
|
+
return;
|
|
3031
|
+
this.options = { ...this.options, visible: true };
|
|
3032
|
+
this.dataSource.show = true;
|
|
3033
|
+
this.startRenderLoop();
|
|
3034
|
+
this.viewer.scene.requestRender();
|
|
3035
|
+
}
|
|
3036
|
+
hide() {
|
|
3037
|
+
if (this.destroyed)
|
|
3038
|
+
return;
|
|
3039
|
+
this.options = { ...this.options, visible: false };
|
|
3040
|
+
this.dataSource.show = false;
|
|
3041
|
+
this.stopRenderLoop();
|
|
3042
|
+
this.viewer.scene.requestRender();
|
|
3043
|
+
}
|
|
3044
|
+
flyTo(options = {}) {
|
|
3045
|
+
if (this.destroyed)
|
|
3046
|
+
return;
|
|
3047
|
+
const center = Cartesian3.fromDegrees(this.options.center.longitude, this.options.center.latitude, this.options.center.height ?? 0);
|
|
3048
|
+
this.viewer.camera.flyToBoundingSphere(new BoundingSphere(center, this.options.radiusMeters), {
|
|
3049
|
+
duration: options.duration ?? 1,
|
|
3050
|
+
offset: new HeadingPitchRange(0, options.pitch ?? -0.52, this.options.radiusMeters * (options.rangeMultiplier ?? 2.65)),
|
|
3051
|
+
});
|
|
3052
|
+
}
|
|
3053
|
+
destroy() {
|
|
3054
|
+
if (this.destroyed)
|
|
3055
|
+
return;
|
|
3056
|
+
this.destroyed = true;
|
|
3057
|
+
this.stopRenderLoop();
|
|
3058
|
+
this.dataSource.entities.removeAll();
|
|
3059
|
+
this.domeEntity = null;
|
|
3060
|
+
this.ringEntity = null;
|
|
3061
|
+
this.material = null;
|
|
3062
|
+
this.viewer.dataSources.remove(this.dataSource, true);
|
|
3063
|
+
this.viewer.scene.requestRender();
|
|
3064
|
+
}
|
|
3065
|
+
isVisible() {
|
|
3066
|
+
return this.options.visible;
|
|
3067
|
+
}
|
|
3068
|
+
isDestroyed() {
|
|
3069
|
+
return this.destroyed;
|
|
3070
|
+
}
|
|
3071
|
+
getOptions() {
|
|
3072
|
+
return {
|
|
3073
|
+
...this.options,
|
|
3074
|
+
center: { ...this.options.center },
|
|
3075
|
+
};
|
|
3076
|
+
}
|
|
3077
|
+
renderEntities() {
|
|
3078
|
+
clearEntities(this.dataSource);
|
|
3079
|
+
this.ringEntity = null;
|
|
3080
|
+
this.material = createShieldDomeMaterialProperty(this.options);
|
|
3081
|
+
this.domeEntity = this.dataSource.entities.add({
|
|
3082
|
+
id: 'geo-effect-kit-shield-dome-shell',
|
|
3083
|
+
position: Cartesian3.fromDegrees(this.options.center.longitude, this.options.center.latitude, this.options.center.height ?? 0),
|
|
3084
|
+
ellipsoid: {
|
|
3085
|
+
radii: new Cartesian3(this.options.radiusMeters, this.options.radiusMeters, this.options.radiusMeters),
|
|
3086
|
+
maximumCone: CesiumMath.PI_OVER_TWO,
|
|
3087
|
+
stackPartitions: 64,
|
|
3088
|
+
slicePartitions: 96,
|
|
3089
|
+
material: this.material,
|
|
3090
|
+
outline: false,
|
|
3091
|
+
},
|
|
3092
|
+
});
|
|
3093
|
+
this.syncRingEntity();
|
|
3094
|
+
}
|
|
3095
|
+
applyMaterialOptions() {
|
|
3096
|
+
if (!this.material)
|
|
3097
|
+
return;
|
|
3098
|
+
this.material.uniforms.color = Color.fromCssColorString(this.options.color).withAlpha(1);
|
|
3099
|
+
this.material.uniforms.opacity = this.options.opacity;
|
|
3100
|
+
this.material.uniforms.speed = this.options.speed;
|
|
3101
|
+
this.material.uniforms.domeType = getShieldDomeTypeUniform(this.options.type);
|
|
3102
|
+
this.material.uniforms.gridDensity = this.options.gridDensity;
|
|
3103
|
+
this.material.uniforms.pulseStrength = this.options.pulseStrength;
|
|
3104
|
+
}
|
|
3105
|
+
syncRingEntity() {
|
|
3106
|
+
if (!this.options.ring) {
|
|
3107
|
+
if (this.ringEntity) {
|
|
3108
|
+
this.dataSource.entities.remove(this.ringEntity);
|
|
3109
|
+
this.ringEntity = null;
|
|
3110
|
+
}
|
|
3111
|
+
return;
|
|
3112
|
+
}
|
|
3113
|
+
const ringMaterial = Color.fromCssColorString(this.options.color).withAlpha(0.11);
|
|
3114
|
+
if (this.ringEntity) {
|
|
3115
|
+
if (this.ringEntity.ellipse) {
|
|
3116
|
+
this.ringEntity.ellipse.semiMajorAxis = new ConstantProperty(this.options.radiusMeters);
|
|
3117
|
+
this.ringEntity.ellipse.semiMinorAxis = new ConstantProperty(this.options.radiusMeters);
|
|
3118
|
+
this.ringEntity.ellipse.material = new ColorMaterialProperty(ringMaterial);
|
|
3119
|
+
this.ringEntity.ellipse.outlineColor = new ConstantProperty(Color.fromCssColorString(this.options.color).withAlpha(0.82));
|
|
3120
|
+
}
|
|
3121
|
+
return;
|
|
3122
|
+
}
|
|
3123
|
+
this.ringEntity = this.dataSource.entities.add({
|
|
3124
|
+
id: 'geo-effect-kit-shield-dome-ground-ring',
|
|
3125
|
+
position: Cartesian3.fromDegrees(this.options.center.longitude, this.options.center.latitude, this.options.center.height ?? 0),
|
|
3126
|
+
ellipse: {
|
|
3127
|
+
semiMajorAxis: this.options.radiusMeters,
|
|
3128
|
+
semiMinorAxis: this.options.radiusMeters,
|
|
3129
|
+
material: ringMaterial,
|
|
3130
|
+
outline: true,
|
|
3131
|
+
outlineColor: Color.fromCssColorString(this.options.color).withAlpha(0.82),
|
|
3132
|
+
outlineWidth: 2,
|
|
3133
|
+
},
|
|
3134
|
+
});
|
|
3135
|
+
}
|
|
3136
|
+
startRenderLoop() {
|
|
3137
|
+
if (this.renderFrame || !this.options.visible || typeof window === 'undefined')
|
|
3138
|
+
return;
|
|
3139
|
+
const tick = () => {
|
|
3140
|
+
if (this.destroyed || !this.options.visible) {
|
|
3141
|
+
this.renderFrame = 0;
|
|
3142
|
+
return;
|
|
3143
|
+
}
|
|
3144
|
+
this.viewer.scene.requestRender();
|
|
3145
|
+
this.renderFrame = window.requestAnimationFrame(tick);
|
|
3146
|
+
};
|
|
3147
|
+
this.renderFrame = window.requestAnimationFrame(tick);
|
|
3148
|
+
}
|
|
3149
|
+
stopRenderLoop() {
|
|
3150
|
+
if (!this.renderFrame || typeof window === 'undefined') {
|
|
3151
|
+
this.renderFrame = 0;
|
|
3152
|
+
return;
|
|
3153
|
+
}
|
|
3154
|
+
window.cancelAnimationFrame(this.renderFrame);
|
|
3155
|
+
this.renderFrame = 0;
|
|
3156
|
+
}
|
|
3157
|
+
}
|
|
3158
|
+
function registerRadarScanMaterial(options) {
|
|
3159
|
+
const cache = Material._materialCache;
|
|
3160
|
+
if (!cache || cache.getMaterial(GEO_RADAR_SCAN_MATERIAL_TYPE))
|
|
3161
|
+
return;
|
|
3162
|
+
cache.addMaterial(GEO_RADAR_SCAN_MATERIAL_TYPE, {
|
|
3163
|
+
fabric: {
|
|
3164
|
+
type: GEO_RADAR_SCAN_MATERIAL_TYPE,
|
|
3165
|
+
uniforms: {
|
|
3166
|
+
color: Color.CYAN,
|
|
3167
|
+
opacity: options.opacity,
|
|
3168
|
+
ringsEnabled: options.rings ? 1 : 0,
|
|
3169
|
+
radarType: getRadarScanTypeUniform(options.type),
|
|
3170
|
+
scanDurationMs: options.scanDurationMs,
|
|
3171
|
+
},
|
|
3172
|
+
source: buildRadarScanMaterialSource(options),
|
|
3173
|
+
},
|
|
3174
|
+
translucent() {
|
|
3175
|
+
return true;
|
|
3176
|
+
},
|
|
3177
|
+
});
|
|
3178
|
+
}
|
|
3179
|
+
function createRadarScanMaterial(options) {
|
|
3180
|
+
const material = Material.fromType(GEO_RADAR_SCAN_MATERIAL_TYPE, {
|
|
3181
|
+
color: Color.fromCssColorString(options.color).withAlpha(1),
|
|
3182
|
+
opacity: options.opacity,
|
|
3183
|
+
ringsEnabled: options.rings ? 1 : 0,
|
|
3184
|
+
radarType: getRadarScanTypeUniform(options.type),
|
|
3185
|
+
scanDurationMs: options.scanDurationMs,
|
|
3186
|
+
});
|
|
3187
|
+
return material;
|
|
3188
|
+
}
|
|
3189
|
+
function registerRippleSpreadMaterial(options) {
|
|
3190
|
+
const cache = Material._materialCache;
|
|
3191
|
+
if (!cache || cache.getMaterial(GEO_RIPPLE_SPREAD_MATERIAL_TYPE))
|
|
3192
|
+
return;
|
|
3193
|
+
cache.addMaterial(GEO_RIPPLE_SPREAD_MATERIAL_TYPE, {
|
|
3194
|
+
fabric: {
|
|
3195
|
+
type: GEO_RIPPLE_SPREAD_MATERIAL_TYPE,
|
|
3196
|
+
uniforms: {
|
|
3197
|
+
color: Color.CYAN,
|
|
3198
|
+
opacity: options.opacity,
|
|
3199
|
+
rippleType: getRippleSpreadTypeUniform(options.type),
|
|
3200
|
+
ringCount: options.ringCount,
|
|
3201
|
+
durationMs: options.durationMs,
|
|
3202
|
+
},
|
|
3203
|
+
source: buildRippleSpreadMaterialSource(),
|
|
3204
|
+
},
|
|
3205
|
+
translucent() {
|
|
3206
|
+
return true;
|
|
3207
|
+
},
|
|
3208
|
+
});
|
|
3209
|
+
}
|
|
3210
|
+
function createRippleSpreadMaterial(options) {
|
|
3211
|
+
const material = Material.fromType(GEO_RIPPLE_SPREAD_MATERIAL_TYPE, {
|
|
3212
|
+
color: Color.fromCssColorString(options.color).withAlpha(1),
|
|
3213
|
+
opacity: options.opacity,
|
|
3214
|
+
rippleType: getRippleSpreadTypeUniform(options.type),
|
|
3215
|
+
ringCount: options.ringCount,
|
|
3216
|
+
durationMs: options.durationMs,
|
|
3217
|
+
});
|
|
3218
|
+
return material;
|
|
3219
|
+
}
|
|
3220
|
+
function registerTemperatureFieldMaterial(options) {
|
|
3221
|
+
registerMaterial(GEO_TEMPERATURE_FIELD_MATERIAL_TYPE, createTemperatureFieldUniforms(options), buildTemperatureFieldMaterialSource());
|
|
3222
|
+
}
|
|
3223
|
+
function createTemperatureFieldMaterial(options) {
|
|
3224
|
+
return Material.fromType(GEO_TEMPERATURE_FIELD_MATERIAL_TYPE, createTemperatureFieldUniforms(options));
|
|
3225
|
+
}
|
|
3226
|
+
function createSceneWeatherUniforms(options) {
|
|
3227
|
+
return {
|
|
3228
|
+
weatherType: getSceneWeatherTypeUniform(options.type),
|
|
3229
|
+
intensity: options.intensity,
|
|
3230
|
+
speed: options.speed,
|
|
3231
|
+
windDirection: options.windDirection,
|
|
3232
|
+
color: Color.fromCssColorString(options.color).withAlpha(1),
|
|
3233
|
+
};
|
|
3234
|
+
}
|
|
3235
|
+
function createPostProcessUniforms(options) {
|
|
3236
|
+
return {
|
|
3237
|
+
effectType: getPostProcessTypeUniform(options.type),
|
|
3238
|
+
strength: options.strength,
|
|
3239
|
+
brightness: options.brightness,
|
|
3240
|
+
contrast: options.contrast,
|
|
3241
|
+
saturation: options.saturation,
|
|
3242
|
+
};
|
|
3243
|
+
}
|
|
3244
|
+
function createTemperatureFieldUniforms(options) {
|
|
3245
|
+
const stops = normalizeTemperatureFieldStops(options.stops);
|
|
3246
|
+
const seedVector = getTemperatureFieldSeedVector(options.seed);
|
|
3247
|
+
const hotSpot0 = getTemperatureFieldSpot(options.seed, 11, 0.14, 0.24);
|
|
3248
|
+
const hotSpot1 = getTemperatureFieldSpot(options.seed, 29, 0.1, 0.18);
|
|
3249
|
+
const coldSpot0 = getTemperatureFieldSpot(options.seed, 47, 0.18, 0.26);
|
|
3250
|
+
return {
|
|
3251
|
+
lowColor: Color.fromCssColorString(stops[0]?.color ?? '#57c7ff').withAlpha(1),
|
|
3252
|
+
lowerColor: Color.fromCssColorString(stops[1]?.color ?? '#6ddb73').withAlpha(1),
|
|
3253
|
+
mediumColor: Color.fromCssColorString(stops[2]?.color ?? '#ffd047').withAlpha(1),
|
|
3254
|
+
higherColor: Color.fromCssColorString(stops[3]?.color ?? '#ff8a2d').withAlpha(1),
|
|
3255
|
+
highColor: Color.fromCssColorString(stops[4]?.color ?? '#ff3e2f').withAlpha(1),
|
|
3256
|
+
opacity: options.opacity,
|
|
3257
|
+
noiseStrength: options.noiseStrength,
|
|
3258
|
+
contourLines: options.contourLines ? 1 : 0,
|
|
3259
|
+
contourStrength: options.contourStrength,
|
|
3260
|
+
seedVector,
|
|
3261
|
+
hotSpot0Center: hotSpot0.center,
|
|
3262
|
+
hotSpot0Radius: hotSpot0.radius,
|
|
3263
|
+
hotSpot0Strength: hotSpot0.strength,
|
|
3264
|
+
hotSpot1Center: hotSpot1.center,
|
|
3265
|
+
hotSpot1Radius: hotSpot1.radius,
|
|
3266
|
+
hotSpot1Strength: hotSpot1.strength,
|
|
3267
|
+
coldSpot0Center: coldSpot0.center,
|
|
3268
|
+
coldSpot0Radius: coldSpot0.radius,
|
|
3269
|
+
coldSpot0Strength: coldSpot0.strength,
|
|
3270
|
+
...createTemperatureFieldSampleUniforms(options),
|
|
3271
|
+
};
|
|
3272
|
+
}
|
|
3273
|
+
class DynamicCesiumMaterialProperty {
|
|
3274
|
+
definitionChanged = new Event();
|
|
3275
|
+
isConstant = false;
|
|
3276
|
+
uniforms;
|
|
3277
|
+
type;
|
|
3278
|
+
constructor(type, uniforms) {
|
|
3279
|
+
this.type = type;
|
|
3280
|
+
this.uniforms = uniforms;
|
|
3281
|
+
}
|
|
3282
|
+
getType(_time) {
|
|
3283
|
+
return this.type;
|
|
3284
|
+
}
|
|
3285
|
+
getValue(_time, result = {}) {
|
|
3286
|
+
return Object.assign(result, this.uniforms);
|
|
3287
|
+
}
|
|
3288
|
+
equals(other) {
|
|
3289
|
+
return this === other;
|
|
3290
|
+
}
|
|
3291
|
+
}
|
|
3292
|
+
function createLightWallMaterialProperty(options) {
|
|
3293
|
+
registerLightWallMaterial();
|
|
3294
|
+
return new DynamicCesiumMaterialProperty(GEO_LIGHT_WALL_MATERIAL_TYPE, {
|
|
3295
|
+
color: Color.fromCssColorString(options.color).withAlpha(1),
|
|
3296
|
+
opacity: options.opacity,
|
|
3297
|
+
speed: options.speed,
|
|
3298
|
+
wallType: getLightWallTypeUniform(options.type),
|
|
3299
|
+
scanLineCount: options.scanLineCount,
|
|
3300
|
+
breathing: options.breathing ? 1 : 0,
|
|
3301
|
+
});
|
|
3302
|
+
}
|
|
3303
|
+
function createScanConeMaterialProperty(options) {
|
|
3304
|
+
registerScanConeMaterial();
|
|
3305
|
+
return new DynamicCesiumMaterialProperty(GEO_SCAN_CONE_MATERIAL_TYPE, {
|
|
3306
|
+
color: Color.fromCssColorString(options.color).withAlpha(1),
|
|
3307
|
+
opacity: options.opacity,
|
|
3308
|
+
speed: options.speed,
|
|
3309
|
+
coneType: getScanConeTypeUniform(options.type),
|
|
3310
|
+
aperture: options.aperture,
|
|
3311
|
+
});
|
|
3312
|
+
}
|
|
3313
|
+
function createShieldDomeMaterialProperty(options) {
|
|
3314
|
+
registerShieldDomeMaterial();
|
|
3315
|
+
return new DynamicCesiumMaterialProperty(GEO_SHIELD_DOME_MATERIAL_TYPE, {
|
|
3316
|
+
color: Color.fromCssColorString(options.color).withAlpha(1),
|
|
3317
|
+
opacity: options.opacity,
|
|
3318
|
+
speed: options.speed,
|
|
3319
|
+
domeType: getShieldDomeTypeUniform(options.type),
|
|
3320
|
+
gridDensity: options.gridDensity,
|
|
3321
|
+
pulseStrength: options.pulseStrength,
|
|
3322
|
+
});
|
|
3323
|
+
}
|
|
3324
|
+
function createWaterSurfaceMaterialProperty(options) {
|
|
3325
|
+
registerWaterSurfaceMaterial();
|
|
3326
|
+
return new DynamicCesiumMaterialProperty(GEO_WATER_SURFACE_MATERIAL_TYPE, createWaterSurfaceUniforms(options));
|
|
3327
|
+
}
|
|
3328
|
+
function createWaterSurfaceUniforms(options) {
|
|
3329
|
+
return {
|
|
3330
|
+
color: Color.fromCssColorString(options.color).withAlpha(1),
|
|
3331
|
+
opacity: options.opacity,
|
|
3332
|
+
speed: options.speed,
|
|
3333
|
+
waterType: getWaterSurfaceTypeUniform(options.type),
|
|
3334
|
+
waveStrength: options.waveStrength,
|
|
3335
|
+
reflectionStrength: options.reflectionStrength,
|
|
3336
|
+
distortionScale: options.distortionScale,
|
|
3337
|
+
reflectivity: options.reflectivity,
|
|
3338
|
+
refractionStrength: options.refractionStrength,
|
|
3339
|
+
fresnelPower: options.fresnelPower,
|
|
3340
|
+
flowDirection: options.flowDirection,
|
|
3341
|
+
};
|
|
3342
|
+
}
|
|
3343
|
+
function registerLightWallMaterial() {
|
|
3344
|
+
registerMaterial(GEO_LIGHT_WALL_MATERIAL_TYPE, {
|
|
3345
|
+
color: Color.CYAN,
|
|
3346
|
+
opacity: 0.72,
|
|
3347
|
+
speed: 1,
|
|
3348
|
+
wallType: 1,
|
|
3349
|
+
scanLineCount: 4,
|
|
3350
|
+
breathing: 1,
|
|
3351
|
+
}, buildLightWallMaterialSource());
|
|
3352
|
+
}
|
|
3353
|
+
function registerScanConeMaterial() {
|
|
3354
|
+
registerMaterial(GEO_SCAN_CONE_MATERIAL_TYPE, {
|
|
3355
|
+
color: Color.CYAN,
|
|
3356
|
+
opacity: 0.62,
|
|
3357
|
+
speed: 1,
|
|
3358
|
+
coneType: 1,
|
|
3359
|
+
aperture: 34,
|
|
3360
|
+
}, buildScanConeMaterialSource());
|
|
3361
|
+
}
|
|
3362
|
+
function registerShieldDomeMaterial() {
|
|
3363
|
+
registerMaterial(GEO_SHIELD_DOME_MATERIAL_TYPE, {
|
|
3364
|
+
color: Color.CYAN,
|
|
3365
|
+
opacity: 0.56,
|
|
3366
|
+
speed: 1,
|
|
3367
|
+
domeType: 1,
|
|
3368
|
+
gridDensity: 14,
|
|
3369
|
+
pulseStrength: 0.72,
|
|
3370
|
+
}, buildShieldDomeMaterialSource());
|
|
3371
|
+
}
|
|
3372
|
+
function registerWaterSurfaceMaterial() {
|
|
3373
|
+
registerMaterial(GEO_WATER_SURFACE_MATERIAL_TYPE, {
|
|
3374
|
+
color: Color.CYAN,
|
|
3375
|
+
opacity: 0.72,
|
|
3376
|
+
speed: 1,
|
|
3377
|
+
waterType: 1,
|
|
3378
|
+
waveStrength: 0.48,
|
|
3379
|
+
reflectionStrength: 0.36,
|
|
3380
|
+
distortionScale: 18,
|
|
3381
|
+
reflectivity: 0.58,
|
|
3382
|
+
refractionStrength: 0.42,
|
|
3383
|
+
fresnelPower: 4,
|
|
3384
|
+
flowDirection: 90,
|
|
3385
|
+
}, buildWaterSurfaceMaterialSource());
|
|
3386
|
+
}
|
|
3387
|
+
function registerMaterial(type, uniforms, source) {
|
|
3388
|
+
const cache = Material._materialCache;
|
|
3389
|
+
if (!cache || cache.getMaterial(type))
|
|
3390
|
+
return;
|
|
3391
|
+
cache.addMaterial(type, {
|
|
3392
|
+
fabric: {
|
|
3393
|
+
type,
|
|
3394
|
+
uniforms,
|
|
3395
|
+
source,
|
|
3396
|
+
},
|
|
3397
|
+
translucent() {
|
|
3398
|
+
return true;
|
|
3399
|
+
},
|
|
3400
|
+
});
|
|
3401
|
+
}
|
|
3402
|
+
function normalizeRadarScanType(type) {
|
|
3403
|
+
return RADAR_SCAN_TYPE_VALUES.includes(type) ? type : 'classic';
|
|
3404
|
+
}
|
|
3405
|
+
function normalizeRippleSpreadType(type) {
|
|
3406
|
+
return RIPPLE_SPREAD_TYPE_VALUES.includes(type) ? type : 'water';
|
|
3407
|
+
}
|
|
3408
|
+
function normalizeSceneWeatherType(type) {
|
|
3409
|
+
return SCENE_WEATHER_TYPE_VALUES.includes(type) ? type : 'rain';
|
|
3410
|
+
}
|
|
3411
|
+
function normalizePostProcessType(type) {
|
|
3412
|
+
return POST_PROCESS_TYPE_VALUES.includes(type) ? type : 'bloom';
|
|
3413
|
+
}
|
|
3414
|
+
function normalizePolylineFlowType(type) {
|
|
3415
|
+
return POLYLINE_FLOW_TYPE_VALUES.includes(type) ? type : 'dispatch';
|
|
3416
|
+
}
|
|
3417
|
+
function normalizeFlyLineMode(mode) {
|
|
3418
|
+
return FLY_LINE_MODE_VALUES.includes(mode) ? mode : 'single-arc';
|
|
3419
|
+
}
|
|
3420
|
+
function normalizeLightWallType(type) {
|
|
3421
|
+
return LIGHT_WALL_TYPE_VALUES.includes(type) ? type : 'security';
|
|
3422
|
+
}
|
|
3423
|
+
function normalizeScanConeType(type) {
|
|
3424
|
+
return SCAN_CONE_TYPE_VALUES.includes(type) ? type : 'searchlight';
|
|
3425
|
+
}
|
|
3426
|
+
function normalizeShieldDomeType(type) {
|
|
3427
|
+
return SHIELD_DOME_TYPE_VALUES.includes(type) ? type : 'hex';
|
|
3428
|
+
}
|
|
3429
|
+
function normalizeWaterSurfaceType(type) {
|
|
3430
|
+
return WATER_SURFACE_TYPE_VALUES.includes(type) ? type : 'river';
|
|
3431
|
+
}
|
|
3432
|
+
function getRadarScanTypeUniform(type) {
|
|
3433
|
+
if (type === 'classic')
|
|
3434
|
+
return 1;
|
|
3435
|
+
if (type === 'sector')
|
|
3436
|
+
return 2;
|
|
3437
|
+
if (type === 'pulse')
|
|
3438
|
+
return 3;
|
|
3439
|
+
return 4;
|
|
3440
|
+
}
|
|
3441
|
+
function getRippleSpreadTypeUniform(type) {
|
|
3442
|
+
if (type === 'water')
|
|
3443
|
+
return 1;
|
|
3444
|
+
if (type === 'energy')
|
|
3445
|
+
return 2;
|
|
3446
|
+
return 3;
|
|
3447
|
+
}
|
|
3448
|
+
function getSceneWeatherTypeUniform(type) {
|
|
3449
|
+
if (type === 'rain')
|
|
3450
|
+
return 1;
|
|
3451
|
+
if (type === 'snow')
|
|
3452
|
+
return 2;
|
|
3453
|
+
if (type === 'fog')
|
|
3454
|
+
return 3;
|
|
3455
|
+
return 4;
|
|
3456
|
+
}
|
|
3457
|
+
function getPostProcessTypeUniform(type) {
|
|
3458
|
+
if (type === 'bloom')
|
|
3459
|
+
return 1;
|
|
3460
|
+
if (type === 'night-vision')
|
|
3461
|
+
return 2;
|
|
3462
|
+
if (type === 'black-white')
|
|
3463
|
+
return 3;
|
|
3464
|
+
if (type === 'brightness')
|
|
3465
|
+
return 4;
|
|
3466
|
+
if (type === 'mosaic')
|
|
3467
|
+
return 5;
|
|
3468
|
+
return 6;
|
|
3469
|
+
}
|
|
3470
|
+
function getLightWallTypeUniform(type) {
|
|
3471
|
+
if (type === 'security')
|
|
3472
|
+
return 1;
|
|
3473
|
+
if (type === 'warning')
|
|
3474
|
+
return 2;
|
|
3475
|
+
if (type === 'data')
|
|
3476
|
+
return 3;
|
|
3477
|
+
if (type === 'fence')
|
|
3478
|
+
return 4;
|
|
3479
|
+
return 5;
|
|
3480
|
+
}
|
|
3481
|
+
function getScanConeTypeUniform(type) {
|
|
3482
|
+
if (type === 'searchlight')
|
|
3483
|
+
return 1;
|
|
3484
|
+
if (type === 'radar')
|
|
3485
|
+
return 2;
|
|
3486
|
+
if (type === 'camera')
|
|
3487
|
+
return 3;
|
|
3488
|
+
if (type === 'drone')
|
|
3489
|
+
return 4;
|
|
3490
|
+
return 5;
|
|
3491
|
+
}
|
|
3492
|
+
function getShieldDomeTypeUniform(type) {
|
|
3493
|
+
if (type === 'hex')
|
|
3494
|
+
return 1;
|
|
3495
|
+
if (type === 'plasma')
|
|
3496
|
+
return 2;
|
|
3497
|
+
if (type === 'matrix')
|
|
3498
|
+
return 3;
|
|
3499
|
+
if (type === 'aegis')
|
|
3500
|
+
return 4;
|
|
3501
|
+
return 5;
|
|
3502
|
+
}
|
|
3503
|
+
function getWaterSurfaceTypeUniform(type) {
|
|
3504
|
+
if (type === 'river')
|
|
3505
|
+
return 1;
|
|
3506
|
+
if (type === 'lake')
|
|
3507
|
+
return 2;
|
|
3508
|
+
return 3;
|
|
3509
|
+
}
|
|
3510
|
+
function getPolylineFlowColor(type, color) {
|
|
3511
|
+
const base = Color.fromCssColorString(color);
|
|
3512
|
+
if (type === 'migration')
|
|
3513
|
+
return Color.lerp(base, Color.fromCssColorString('#b8ff58'), 0.28, new Color());
|
|
3514
|
+
if (type === 'attack')
|
|
3515
|
+
return Color.lerp(base, Color.fromCssColorString('#ff315a'), 0.42, new Color());
|
|
3516
|
+
if (type === 'comet')
|
|
3517
|
+
return Color.lerp(base, Color.fromCssColorString('#ffffff'), 0.32, new Color());
|
|
3518
|
+
if (type === 'electric')
|
|
3519
|
+
return Color.lerp(base, Color.fromCssColorString('#9b6dff'), 0.38, new Color());
|
|
3520
|
+
return base;
|
|
3521
|
+
}
|
|
3522
|
+
function getPolylineFlowProfile(type) {
|
|
3523
|
+
if (type === 'migration')
|
|
3524
|
+
return { tail: 2, power: 1 };
|
|
3525
|
+
if (type === 'attack')
|
|
3526
|
+
return { tail: 1.6, power: 1.35 };
|
|
3527
|
+
if (type === 'comet')
|
|
3528
|
+
return { tail: 1.72, power: 1 };
|
|
3529
|
+
if (type === 'electric')
|
|
3530
|
+
return { tail: 1.35, power: 0.72 };
|
|
3531
|
+
return { tail: 1.72, power: 1 };
|
|
3532
|
+
}
|
|
3533
|
+
function getTrailSampleCount(type) {
|
|
3534
|
+
if (type === 'attack')
|
|
3535
|
+
return 5;
|
|
3536
|
+
if (type === 'electric')
|
|
3537
|
+
return 9;
|
|
3538
|
+
if (type === 'migration')
|
|
3539
|
+
return 8;
|
|
3540
|
+
return 7;
|
|
3541
|
+
}
|
|
3542
|
+
function getFlyLineBaseSampleCount(mode) {
|
|
3543
|
+
if (mode === 'hub-spoke')
|
|
3544
|
+
return 28;
|
|
3545
|
+
if (mode === 'bidirectional')
|
|
3546
|
+
return 30;
|
|
3547
|
+
return 26;
|
|
3548
|
+
}
|
|
3549
|
+
function getFlyLineTrailSampleCount(mode) {
|
|
3550
|
+
if (mode === 'hub-spoke')
|
|
3551
|
+
return 8;
|
|
3552
|
+
if (mode === 'bidirectional')
|
|
3553
|
+
return 7;
|
|
3554
|
+
return 6;
|
|
3555
|
+
}
|
|
3556
|
+
function getFlyLineSpeedScale(mode) {
|
|
3557
|
+
if (mode === 'hub-spoke')
|
|
3558
|
+
return 0.24;
|
|
3559
|
+
if (mode === 'bidirectional')
|
|
3560
|
+
return 0.28;
|
|
3561
|
+
return 0.22;
|
|
3562
|
+
}
|
|
3563
|
+
function getFlyLineBaseColor(mode, color) {
|
|
3564
|
+
const base = Color.fromCssColorString(color);
|
|
3565
|
+
if (mode === 'hub-spoke')
|
|
3566
|
+
return Color.lerp(base, Color.fromCssColorString('#ff58c8'), 0.18, new Color());
|
|
3567
|
+
if (mode === 'bidirectional')
|
|
3568
|
+
return Color.lerp(base, Color.WHITE, 0.16, new Color());
|
|
3569
|
+
return base;
|
|
3570
|
+
}
|
|
3571
|
+
function getFlyLineTrailColor(mode, color) {
|
|
3572
|
+
const base = Color.fromCssColorString(color);
|
|
3573
|
+
if (mode === 'hub-spoke')
|
|
3574
|
+
return Color.lerp(base, Color.fromCssColorString('#ff58c8'), 0.34, new Color());
|
|
3575
|
+
if (mode === 'bidirectional')
|
|
3576
|
+
return Color.lerp(base, Color.fromCssColorString('#e8ff72'), 0.3, new Color());
|
|
3577
|
+
return Color.lerp(base, Color.WHITE, 0.18, new Color());
|
|
3578
|
+
}
|
|
3579
|
+
function getPipeLayerWidth(width, multiplier) {
|
|
3580
|
+
return Math.max(1, Math.round(width * multiplier * 1000) / 1000);
|
|
3581
|
+
}
|
|
3582
|
+
function getConeBottomRadius(options) {
|
|
3583
|
+
const apertureRadius = Math.tan(CesiumMath.toRadians(options.aperture) / 2) * options.lengthMeters;
|
|
3584
|
+
return Math.max(options.radiusMeters, apertureRadius);
|
|
3585
|
+
}
|
|
3586
|
+
function normalizePositions(positions, close) {
|
|
3587
|
+
const normalized = (positions.length ? positions : [{ longitude: 0, latitude: 0 }]).map(normalizePosition);
|
|
3588
|
+
const onlyPosition = normalized[0];
|
|
3589
|
+
if (normalized.length === 1 && onlyPosition)
|
|
3590
|
+
normalized.push({ ...onlyPosition });
|
|
3591
|
+
if (close && normalized.length > 2) {
|
|
3592
|
+
const first = normalized[0];
|
|
3593
|
+
const last = normalized[normalized.length - 1];
|
|
3594
|
+
if (first && last && !positionEqual(first, last))
|
|
3595
|
+
normalized.push({ ...first });
|
|
3596
|
+
}
|
|
3597
|
+
return normalized;
|
|
3598
|
+
}
|
|
3599
|
+
function normalizeFlyLineRoutes(lines) {
|
|
3600
|
+
const fallback = {
|
|
3601
|
+
from: { longitude: 0, latitude: 0 },
|
|
3602
|
+
to: { longitude: 0, latitude: 0 },
|
|
3603
|
+
};
|
|
3604
|
+
return (lines.length ? lines : [fallback]).map((line) => ({
|
|
3605
|
+
from: normalizePosition(line.from),
|
|
3606
|
+
to: normalizePosition(line.to),
|
|
3607
|
+
}));
|
|
3608
|
+
}
|
|
3609
|
+
function normalizePosition(position) {
|
|
3610
|
+
const normalized = {
|
|
3611
|
+
longitude: finiteOr(position.longitude, 0),
|
|
3612
|
+
latitude: finiteOr(position.latitude, 0),
|
|
3613
|
+
};
|
|
3614
|
+
if (position.height !== undefined)
|
|
3615
|
+
normalized.height = finiteOr(position.height, 0);
|
|
3616
|
+
return normalized;
|
|
3617
|
+
}
|
|
3618
|
+
function normalizeFireBillboardPoints(points) {
|
|
3619
|
+
return points
|
|
3620
|
+
.filter((point) => typeof point.gif === 'string' && point.gif.trim().length > 0)
|
|
3621
|
+
.map((point) => {
|
|
3622
|
+
const normalized = {
|
|
3623
|
+
longitude: finiteOr(point.longitude, 0),
|
|
3624
|
+
latitude: finiteOr(point.latitude, 0),
|
|
3625
|
+
height: finiteOr(point.height ?? 0, 0),
|
|
3626
|
+
gif: point.gif.trim(),
|
|
3627
|
+
};
|
|
3628
|
+
if (point.id)
|
|
3629
|
+
normalized.id = point.id;
|
|
3630
|
+
if (point.label)
|
|
3631
|
+
normalized.label = point.label;
|
|
3632
|
+
return normalized;
|
|
3633
|
+
});
|
|
3634
|
+
}
|
|
3635
|
+
function cloneFireBillboardPoints(points) {
|
|
3636
|
+
return points.map((point) => ({ ...point }));
|
|
3637
|
+
}
|
|
3638
|
+
function fireBillboardPointsEqual(left, right) {
|
|
3639
|
+
if (left.length !== right.length)
|
|
3640
|
+
return false;
|
|
3641
|
+
return left.every((point, index) => {
|
|
3642
|
+
const other = right[index];
|
|
3643
|
+
return (other !== undefined &&
|
|
3644
|
+
point.longitude === other.longitude &&
|
|
3645
|
+
point.latitude === other.latitude &&
|
|
3646
|
+
(point.height ?? 0) === (other.height ?? 0) &&
|
|
3647
|
+
point.gif === other.gif &&
|
|
3648
|
+
point.id === other.id &&
|
|
3649
|
+
point.label === other.label);
|
|
3650
|
+
});
|
|
3651
|
+
}
|
|
3652
|
+
function normalizeTemperatureFieldPolygons(polygons) {
|
|
3653
|
+
return polygons
|
|
3654
|
+
.map((polygon) => ({
|
|
3655
|
+
outer: normalizeTemperatureRing(polygon.outer),
|
|
3656
|
+
holes: (polygon.holes ?? []).map(normalizeTemperatureRing).filter((hole) => hole.length >= 3),
|
|
3657
|
+
}))
|
|
3658
|
+
.filter((polygon) => polygon.outer.length >= 3);
|
|
3659
|
+
}
|
|
3660
|
+
function normalizeTemperatureRing(ring) {
|
|
3661
|
+
return ring.map(([longitude, latitude]) => [finiteOr(longitude, 0), finiteOr(latitude, 0)]);
|
|
3662
|
+
}
|
|
3663
|
+
function normalizeTemperatureFieldStops(stops = getDefaultTemperatureFieldStops()) {
|
|
3664
|
+
const sortedStops = [...(stops.length ? stops : getDefaultTemperatureFieldStops())]
|
|
3665
|
+
.map((stop) => ({
|
|
3666
|
+
value: clamp(finiteOr(stop.value, 0), 0, 100),
|
|
3667
|
+
color: stop.color || '#57c7ff',
|
|
3668
|
+
label: stop.label ?? '',
|
|
3669
|
+
}))
|
|
3670
|
+
.sort((a, b) => a.value - b.value);
|
|
3671
|
+
if (sortedStops.length >= 5) {
|
|
3672
|
+
return sortedStops.slice(0, 5);
|
|
3673
|
+
}
|
|
3674
|
+
const defaults = getDefaultTemperatureFieldStops();
|
|
3675
|
+
return defaults.map((fallback, index) => sortedStops[index] ?? fallback);
|
|
3676
|
+
}
|
|
3677
|
+
function getDefaultTemperatureFieldStops() {
|
|
3678
|
+
return [
|
|
3679
|
+
{ value: 0, color: '#57c7ff', label: 'low' },
|
|
3680
|
+
{ value: 20, color: '#6ddb73', label: 'lower' },
|
|
3681
|
+
{ value: 40, color: '#ffd047', label: 'medium' },
|
|
3682
|
+
{ value: 60, color: '#ff8a2d', label: 'higher' },
|
|
3683
|
+
{ value: 100, color: '#ff3e2f', label: 'high' },
|
|
3684
|
+
];
|
|
3685
|
+
}
|
|
3686
|
+
function normalizeTemperatureFieldSamples(samples = []) {
|
|
3687
|
+
return samples
|
|
3688
|
+
.map((sample) => ({
|
|
3689
|
+
longitude: finiteOr(sample.longitude, 0),
|
|
3690
|
+
latitude: finiteOr(sample.latitude, 0),
|
|
3691
|
+
value: clamp(finiteOr(sample.value, 0), 0, 100),
|
|
3692
|
+
type: sample.type ?? '',
|
|
3693
|
+
}))
|
|
3694
|
+
.filter((sample) => sample.longitude >= -180 && sample.longitude <= 180 && sample.latitude >= -90 && sample.latitude <= 90)
|
|
3695
|
+
.slice(0, 16);
|
|
3696
|
+
}
|
|
3697
|
+
function normalizeTemperatureFieldBounds(bounds) {
|
|
3698
|
+
if (!bounds)
|
|
3699
|
+
return null;
|
|
3700
|
+
return {
|
|
3701
|
+
west: finiteOr(bounds.west, 0),
|
|
3702
|
+
south: finiteOr(bounds.south, 0),
|
|
3703
|
+
east: finiteOr(bounds.east, 0),
|
|
3704
|
+
north: finiteOr(bounds.north, 0),
|
|
3705
|
+
};
|
|
3706
|
+
}
|
|
3707
|
+
function getTemperatureFieldBounds(polygons) {
|
|
3708
|
+
const points = polygons.flatMap((polygon) => [polygon.outer, ...polygon.holes]).flat();
|
|
3709
|
+
if (points.length === 0)
|
|
3710
|
+
return null;
|
|
3711
|
+
return points.reduce((bounds, [longitude, latitude]) => ({
|
|
3712
|
+
west: Math.min(bounds.west, longitude),
|
|
3713
|
+
south: Math.min(bounds.south, latitude),
|
|
3714
|
+
east: Math.max(bounds.east, longitude),
|
|
3715
|
+
north: Math.max(bounds.north, latitude),
|
|
3716
|
+
}), {
|
|
3717
|
+
west: Number.POSITIVE_INFINITY,
|
|
3718
|
+
south: Number.POSITIVE_INFINITY,
|
|
3719
|
+
east: Number.NEGATIVE_INFINITY,
|
|
3720
|
+
north: Number.NEGATIVE_INFINITY,
|
|
3721
|
+
});
|
|
3722
|
+
}
|
|
3723
|
+
function normalizeSeed(seed) {
|
|
3724
|
+
const value = finiteOr(seed ?? 0, 0);
|
|
3725
|
+
return Math.max(0, Math.floor(value));
|
|
3726
|
+
}
|
|
3727
|
+
function temperatureFieldPolygonsEqual(left, right) {
|
|
3728
|
+
if (left.length !== right.length)
|
|
3729
|
+
return false;
|
|
3730
|
+
return left.every((polygon, index) => {
|
|
3731
|
+
const other = right[index];
|
|
3732
|
+
if (!other)
|
|
3733
|
+
return false;
|
|
3734
|
+
if (!temperatureRingsEqual(polygon.outer, other.outer))
|
|
3735
|
+
return false;
|
|
3736
|
+
if (polygon.holes.length !== other.holes.length)
|
|
3737
|
+
return false;
|
|
3738
|
+
return polygon.holes.every((hole, holeIndex) => temperatureRingsEqual(hole, other.holes[holeIndex] ?? []));
|
|
3739
|
+
});
|
|
3740
|
+
}
|
|
3741
|
+
function temperatureRingsEqual(left, right) {
|
|
3742
|
+
if (left.length !== right.length)
|
|
3743
|
+
return false;
|
|
3744
|
+
return left.every((point, index) => {
|
|
3745
|
+
const other = right[index];
|
|
3746
|
+
return other !== undefined && point[0] === other[0] && point[1] === other[1];
|
|
3747
|
+
});
|
|
3748
|
+
}
|
|
3749
|
+
function cloneTemperatureFieldOptions(options) {
|
|
3750
|
+
return {
|
|
3751
|
+
...options,
|
|
3752
|
+
bounds: options.bounds ? { ...options.bounds } : null,
|
|
3753
|
+
polygons: options.polygons.map((polygon) => ({
|
|
3754
|
+
outer: polygon.outer.map(([longitude, latitude]) => [longitude, latitude]),
|
|
3755
|
+
holes: polygon.holes.map((hole) => hole.map(([longitude, latitude]) => [longitude, latitude])),
|
|
3756
|
+
})),
|
|
3757
|
+
stops: options.stops.map((stop) => ({ ...stop })),
|
|
3758
|
+
samples: options.samples.map((sample) => ({ ...sample })),
|
|
3759
|
+
};
|
|
3760
|
+
}
|
|
3761
|
+
function isTemperatureRingClosed(ring) {
|
|
3762
|
+
const first = ring[0];
|
|
3763
|
+
const last = ring[ring.length - 1];
|
|
3764
|
+
return Boolean(first && last && first[0] === last[0] && first[1] === last[1]);
|
|
3765
|
+
}
|
|
3766
|
+
function getTemperatureFieldSeedVector(seed) {
|
|
3767
|
+
const angle = seededUnit(seed, 3) * Math.PI * 2;
|
|
3768
|
+
return new Cartesian4(Math.cos(angle), Math.sin(angle), seededUnit(seed, 7), seededUnit(seed, 13));
|
|
3769
|
+
}
|
|
3770
|
+
function getTemperatureFieldSpot(seed, offset, minRadius, maxRadius) {
|
|
3771
|
+
return {
|
|
3772
|
+
center: new Cartesian2(0.18 + seededUnit(seed, offset) * 0.64, 0.16 + seededUnit(seed, offset + 5) * 0.68),
|
|
3773
|
+
radius: minRadius + seededUnit(seed, offset + 11) * (maxRadius - minRadius),
|
|
3774
|
+
strength: 0.12 + seededUnit(seed, offset + 17) * 0.24,
|
|
3775
|
+
};
|
|
3776
|
+
}
|
|
3777
|
+
function createTemperatureFieldSampleUniforms(options) {
|
|
3778
|
+
const uniforms = {
|
|
3779
|
+
sampleCount: options.bounds ? options.samples.length : 0,
|
|
3780
|
+
};
|
|
3781
|
+
for (let index = 0; index < 16; index += 1) {
|
|
3782
|
+
const sample = options.samples[index];
|
|
3783
|
+
uniforms[`sample${index}`] =
|
|
3784
|
+
sample && options.bounds ? getTemperatureFieldSampleVector(sample, options.bounds, index) : new Cartesian4(0, 0, 0, 0);
|
|
3785
|
+
}
|
|
3786
|
+
return uniforms;
|
|
3787
|
+
}
|
|
3788
|
+
function getTemperatureFieldSampleVector(sample, bounds, index) {
|
|
3789
|
+
const width = Math.max(0.000001, bounds.east - bounds.west);
|
|
3790
|
+
const height = Math.max(0.000001, bounds.north - bounds.south);
|
|
3791
|
+
return new Cartesian4(clamp01((sample.longitude - bounds.west) / width), clamp01((sample.latitude - bounds.south) / height), sample.value / 100, 0.4 + seededUnit(Math.round(sample.value * 10), index + 101) * 0.6);
|
|
3792
|
+
}
|
|
3793
|
+
function seededUnit(seed, offset) {
|
|
3794
|
+
const value = Math.sin((seed + 1) * (offset + 12.9898) * 78.233) * 43_758.5453;
|
|
3795
|
+
return fract(value);
|
|
3796
|
+
}
|
|
3797
|
+
function clonePositions(positions) {
|
|
3798
|
+
return positions.map((position) => ({ ...position }));
|
|
3799
|
+
}
|
|
3800
|
+
function cloneFlyLineRoutes(lines) {
|
|
3801
|
+
return lines.map((line) => ({
|
|
3802
|
+
from: { ...line.from },
|
|
3803
|
+
to: { ...line.to },
|
|
3804
|
+
}));
|
|
3805
|
+
}
|
|
3806
|
+
function positionsEqual(left, right) {
|
|
3807
|
+
if (left.length !== right.length)
|
|
3808
|
+
return false;
|
|
3809
|
+
return left.every((position, index) => {
|
|
3810
|
+
const other = right[index];
|
|
3811
|
+
return other !== undefined && positionEqual(position, other);
|
|
3812
|
+
});
|
|
3813
|
+
}
|
|
3814
|
+
function flyLineRoutesEqual(left, right) {
|
|
3815
|
+
if (left.length !== right.length)
|
|
3816
|
+
return false;
|
|
3817
|
+
return left.every((line, index) => {
|
|
3818
|
+
const other = right[index];
|
|
3819
|
+
return other !== undefined && positionEqual(line.from, other.from) && positionEqual(line.to, other.to);
|
|
3820
|
+
});
|
|
3821
|
+
}
|
|
3822
|
+
function uniqueFlyLineEndpoints(lines) {
|
|
3823
|
+
const endpoints = [];
|
|
3824
|
+
normalizeFlyLineRoutes(lines).forEach((line) => {
|
|
3825
|
+
pushDistinctEndpoint(endpoints, line.from);
|
|
3826
|
+
pushDistinctEndpoint(endpoints, line.to);
|
|
3827
|
+
});
|
|
3828
|
+
return endpoints;
|
|
3829
|
+
}
|
|
3830
|
+
function pushDistinctEndpoint(endpoints, position) {
|
|
3831
|
+
if (!endpoints.some((endpoint) => positionEqual(endpoint, position)))
|
|
3832
|
+
endpoints.push({ ...position });
|
|
3833
|
+
}
|
|
3834
|
+
function positionEqual(left, right) {
|
|
3835
|
+
return (left.longitude === right.longitude &&
|
|
3836
|
+
left.latitude === right.latitude &&
|
|
3837
|
+
(left.height ?? 0) === (right.height ?? 0));
|
|
3838
|
+
}
|
|
3839
|
+
function positionsToCartesians(positions) {
|
|
3840
|
+
return positions.map((position) => Cartesian3.fromDegrees(position.longitude, position.latitude, position.height ?? 0));
|
|
3841
|
+
}
|
|
3842
|
+
function positionsToCartesiansAtHeight(positions, heightOffset) {
|
|
3843
|
+
return positions.map((position) => Cartesian3.fromDegrees(position.longitude, position.latitude, (position.height ?? 0) + heightOffset));
|
|
3844
|
+
}
|
|
3845
|
+
function roundPolylineCorners(positions, radius) {
|
|
3846
|
+
if (radius <= 0 || positions.length < 3)
|
|
3847
|
+
return positions;
|
|
3848
|
+
const rounded = [{ ...(positions[0] ?? { longitude: 0, latitude: 0 }) }];
|
|
3849
|
+
for (let index = 1; index < positions.length - 1; index += 1) {
|
|
3850
|
+
const previous = positions[index - 1];
|
|
3851
|
+
const corner = positions[index];
|
|
3852
|
+
const next = positions[index + 1];
|
|
3853
|
+
if (!previous || !corner || !next || positionEqual(previous, corner) || positionEqual(corner, next)) {
|
|
3854
|
+
if (corner)
|
|
3855
|
+
rounded.push({ ...corner });
|
|
3856
|
+
continue;
|
|
3857
|
+
}
|
|
3858
|
+
const before = lerpPosition(corner, previous, radius);
|
|
3859
|
+
const after = lerpPosition(corner, next, radius);
|
|
3860
|
+
pushDistinctPosition(rounded, before);
|
|
3861
|
+
for (let step = 1; step < 6; step += 1) {
|
|
3862
|
+
const amount = step / 6;
|
|
3863
|
+
pushDistinctPosition(rounded, quadraticBezierPosition(before, corner, after, amount));
|
|
3864
|
+
}
|
|
3865
|
+
pushDistinctPosition(rounded, after);
|
|
3866
|
+
}
|
|
3867
|
+
pushDistinctPosition(rounded, { ...(positions[positions.length - 1] ?? positions[0] ?? { longitude: 0, latitude: 0 }) });
|
|
3868
|
+
return rounded;
|
|
3869
|
+
}
|
|
3870
|
+
function lerpPosition(start, end, amount) {
|
|
3871
|
+
const t = clamp01(amount);
|
|
3872
|
+
return {
|
|
3873
|
+
longitude: lerp(start.longitude, end.longitude, t),
|
|
3874
|
+
latitude: lerp(start.latitude, end.latitude, t),
|
|
3875
|
+
height: lerp(start.height ?? 0, end.height ?? 0, t),
|
|
3876
|
+
};
|
|
3877
|
+
}
|
|
3878
|
+
function quadraticBezierPosition(start, control, end, amount) {
|
|
3879
|
+
const t = clamp01(amount);
|
|
3880
|
+
const inverse = 1 - t;
|
|
3881
|
+
return {
|
|
3882
|
+
longitude: inverse * inverse * start.longitude + 2 * inverse * t * control.longitude + t * t * end.longitude,
|
|
3883
|
+
latitude: inverse * inverse * start.latitude + 2 * inverse * t * control.latitude + t * t * end.latitude,
|
|
3884
|
+
height: inverse * inverse * (start.height ?? 0) + 2 * inverse * t * (control.height ?? 0) + t * t * (end.height ?? 0),
|
|
3885
|
+
};
|
|
3886
|
+
}
|
|
3887
|
+
function pushDistinctPosition(positions, position) {
|
|
3888
|
+
const previous = positions[positions.length - 1];
|
|
3889
|
+
if (!previous || !positionEqual(previous, position))
|
|
3890
|
+
positions.push(position);
|
|
3891
|
+
}
|
|
3892
|
+
function clearEntities(dataSource) {
|
|
3893
|
+
if (dataSource.entities.values.length > 0)
|
|
3894
|
+
dataSource.entities.removeAll();
|
|
3895
|
+
}
|
|
3896
|
+
const gifFrameImageCache = new Map();
|
|
3897
|
+
const GIF_FETCH_TIMEOUT_MS = 5000;
|
|
3898
|
+
function loadGifFrameImages(gif) {
|
|
3899
|
+
const cached = gifFrameImageCache.get(gif);
|
|
3900
|
+
if (cached)
|
|
3901
|
+
return cached;
|
|
3902
|
+
const promise = decodeGifFrameImages(gif).catch(() => {
|
|
3903
|
+
gifFrameImageCache.delete(gif);
|
|
3904
|
+
return [gif];
|
|
3905
|
+
});
|
|
3906
|
+
gifFrameImageCache.set(gif, promise);
|
|
3907
|
+
return promise;
|
|
3908
|
+
}
|
|
3909
|
+
async function decodeGifFrameImages(gif) {
|
|
3910
|
+
if (typeof fetch !== 'function' || typeof document === 'undefined')
|
|
3911
|
+
return [gif];
|
|
3912
|
+
const response = await fetchWithTimeout(gif, GIF_FETCH_TIMEOUT_MS);
|
|
3913
|
+
if (!response.ok)
|
|
3914
|
+
throw new Error(`Failed to load GIF: ${response.status}`);
|
|
3915
|
+
const buffer = await response.arrayBuffer();
|
|
3916
|
+
const parsed = parseGIF(buffer);
|
|
3917
|
+
const frames = decompressFrames(parsed, true);
|
|
3918
|
+
if (frames.length <= 1)
|
|
3919
|
+
return [gif];
|
|
3920
|
+
return renderGifFramesToDataUrls(frames, parsed.lsd.width, parsed.lsd.height, gif);
|
|
3921
|
+
}
|
|
3922
|
+
function fetchWithTimeout(url, timeoutMs) {
|
|
3923
|
+
if (typeof AbortController !== 'function')
|
|
3924
|
+
return fetch(url);
|
|
3925
|
+
const controller = new AbortController();
|
|
3926
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
3927
|
+
return fetch(url, { signal: controller.signal }).finally(() => {
|
|
3928
|
+
clearTimeout(timer);
|
|
3929
|
+
});
|
|
3930
|
+
}
|
|
3931
|
+
function renderGifFramesToDataUrls(frames, width, height, fallback) {
|
|
3932
|
+
const canvas = document.createElement('canvas');
|
|
3933
|
+
canvas.width = Math.max(1, finiteOr(width, 1));
|
|
3934
|
+
canvas.height = Math.max(1, finiteOr(height, 1));
|
|
3935
|
+
const context = canvas.getContext('2d');
|
|
3936
|
+
if (!context)
|
|
3937
|
+
return [fallback];
|
|
3938
|
+
const previousCanvas = document.createElement('canvas');
|
|
3939
|
+
previousCanvas.width = canvas.width;
|
|
3940
|
+
previousCanvas.height = canvas.height;
|
|
3941
|
+
const previousContext = previousCanvas.getContext('2d');
|
|
3942
|
+
const imageUrls = [];
|
|
3943
|
+
frames.forEach((frame) => {
|
|
3944
|
+
const previousImage = frame.disposalType === 3 && previousContext ? context.getImageData(0, 0, canvas.width, canvas.height) : null;
|
|
3945
|
+
const imageData = context.createImageData(frame.dims.width, frame.dims.height);
|
|
3946
|
+
imageData.data.set(frame.patch);
|
|
3947
|
+
context.putImageData(imageData, frame.dims.left, frame.dims.top);
|
|
3948
|
+
imageUrls.push(canvas.toDataURL('image/png'));
|
|
3949
|
+
if (frame.disposalType === 2) {
|
|
3950
|
+
context.clearRect(frame.dims.left, frame.dims.top, frame.dims.width, frame.dims.height);
|
|
3951
|
+
}
|
|
3952
|
+
else if (frame.disposalType === 3 && previousImage && previousContext) {
|
|
3953
|
+
previousContext.putImageData(previousImage, 0, 0);
|
|
3954
|
+
context.clearRect(0, 0, canvas.width, canvas.height);
|
|
3955
|
+
context.drawImage(previousCanvas, 0, 0);
|
|
3956
|
+
}
|
|
3957
|
+
});
|
|
3958
|
+
return imageUrls.length > 0 ? imageUrls : [fallback];
|
|
3959
|
+
}
|
|
3960
|
+
function getPositionBounds(positions, fallbackRadius) {
|
|
3961
|
+
const cartesians = positionsToCartesians(positions);
|
|
3962
|
+
const sphere = BoundingSphere.fromPoints(cartesians);
|
|
3963
|
+
return {
|
|
3964
|
+
center: sphere.center,
|
|
3965
|
+
radius: Math.max(fallbackRadius, sphere.radius),
|
|
3966
|
+
};
|
|
3967
|
+
}
|
|
3968
|
+
export function expandFlyLineRoutes(lines, mode = 'single-arc') {
|
|
3969
|
+
const routes = normalizeFlyLineRoutes(lines);
|
|
3970
|
+
if (normalizeFlyLineMode(mode) !== 'bidirectional')
|
|
3971
|
+
return cloneFlyLineRoutes(routes);
|
|
3972
|
+
return routes.flatMap((line) => [
|
|
3973
|
+
{ from: { ...line.from }, to: { ...line.to } },
|
|
3974
|
+
{ from: { ...line.to }, to: { ...line.from } },
|
|
3975
|
+
]);
|
|
3976
|
+
}
|
|
3977
|
+
export function sampleFlyLineArc(line, arcHeight = 38000, sampleCount = 28) {
|
|
3978
|
+
const route = normalizeFlyLineRoutes([line])[0];
|
|
3979
|
+
if (!route)
|
|
3980
|
+
return [];
|
|
3981
|
+
const samples = clampInteger(finiteOr(sampleCount, 28), 2, 96);
|
|
3982
|
+
const height = Math.max(0, finiteOr(arcHeight, 0));
|
|
3983
|
+
const fromHeight = route.from.height ?? 0;
|
|
3984
|
+
const toHeight = route.to.height ?? 0;
|
|
3985
|
+
const control = {
|
|
3986
|
+
longitude: (route.from.longitude + route.to.longitude) / 2,
|
|
3987
|
+
latitude: (route.from.latitude + route.to.latitude) / 2,
|
|
3988
|
+
height: Math.max(fromHeight, toHeight) + height * 2,
|
|
3989
|
+
};
|
|
3990
|
+
return Array.from({ length: samples }, (_, index) => {
|
|
3991
|
+
if (index === 0)
|
|
3992
|
+
return { ...route.from };
|
|
3993
|
+
if (index === samples - 1)
|
|
3994
|
+
return { ...route.to };
|
|
3995
|
+
const amount = index / Math.max(1, samples - 1);
|
|
3996
|
+
return quadraticBezierPosition(route.from, control, route.to, amount);
|
|
3997
|
+
});
|
|
3998
|
+
}
|
|
3999
|
+
export function sampleFlyLineTrail(line, arcHeight, progress, trailLength, sampleCount) {
|
|
4000
|
+
const arc = sampleFlyLineArc(line, arcHeight, Math.max(24, sampleCount * 4));
|
|
4001
|
+
return samplePolylineTrail(arc, progress, trailLength, sampleCount);
|
|
4002
|
+
}
|
|
4003
|
+
function samplePolylineTrail(positions, progress, trailLength, sampleCount) {
|
|
4004
|
+
const safePositions = normalizePositions(positions, false);
|
|
4005
|
+
const samples = clampInteger(sampleCount, 2, 16);
|
|
4006
|
+
const distances = getPolylineCumulativeDistances(safePositions);
|
|
4007
|
+
const totalDistance = distances[distances.length - 1] ?? 0;
|
|
4008
|
+
if (totalDistance <= 0)
|
|
4009
|
+
return safePositions;
|
|
4010
|
+
const head = fract(progress) * totalDistance;
|
|
4011
|
+
const length = clamp01(trailLength) * totalDistance;
|
|
4012
|
+
const start = Math.max(0, head - length);
|
|
4013
|
+
const sampledDistances = Array.from({ length: samples }, (_, index) => {
|
|
4014
|
+
const amount = index / Math.max(1, samples - 1);
|
|
4015
|
+
return lerp(start, head, amount);
|
|
4016
|
+
});
|
|
4017
|
+
const vertexDistances = distances.filter((distance) => distance > start && distance < head);
|
|
4018
|
+
const targetDistances = uniqueSortedDistances([...sampledDistances, ...vertexDistances]);
|
|
4019
|
+
return targetDistances.map((distance) => interpolatePolylinePosition(safePositions, distances, distance));
|
|
4020
|
+
}
|
|
4021
|
+
function getPolylineCumulativeDistances(positions) {
|
|
4022
|
+
const distances = [0];
|
|
4023
|
+
for (let index = 1; index < positions.length; index += 1) {
|
|
4024
|
+
const previous = positions[index - 1];
|
|
4025
|
+
const current = positions[index];
|
|
4026
|
+
if (!previous || !current)
|
|
4027
|
+
continue;
|
|
4028
|
+
const previousCartesian = Cartesian3.fromDegrees(previous.longitude, previous.latitude, previous.height ?? 0);
|
|
4029
|
+
const currentCartesian = Cartesian3.fromDegrees(current.longitude, current.latitude, current.height ?? 0);
|
|
4030
|
+
distances.push((distances[distances.length - 1] ?? 0) + Cartesian3.distance(previousCartesian, currentCartesian));
|
|
4031
|
+
}
|
|
4032
|
+
return distances;
|
|
4033
|
+
}
|
|
4034
|
+
function interpolatePolylinePosition(positions, distances, targetDistance) {
|
|
4035
|
+
if (targetDistance <= 0)
|
|
4036
|
+
return { ...(positions[0] ?? { longitude: 0, latitude: 0 }) };
|
|
4037
|
+
for (let index = 1; index < distances.length; index += 1) {
|
|
4038
|
+
const previousDistance = distances[index - 1] ?? 0;
|
|
4039
|
+
const currentDistance = distances[index] ?? previousDistance;
|
|
4040
|
+
if (targetDistance > currentDistance)
|
|
4041
|
+
continue;
|
|
4042
|
+
const previous = positions[index - 1] ?? positions[0];
|
|
4043
|
+
const current = positions[index] ?? previous;
|
|
4044
|
+
if (!previous || !current)
|
|
4045
|
+
return { longitude: 0, latitude: 0 };
|
|
4046
|
+
const span = Math.max(1, currentDistance - previousDistance);
|
|
4047
|
+
const t = clamp01((targetDistance - previousDistance) / span);
|
|
4048
|
+
return {
|
|
4049
|
+
longitude: lerp(previous.longitude, current.longitude, t),
|
|
4050
|
+
latitude: lerp(previous.latitude, current.latitude, t),
|
|
4051
|
+
height: lerp(previous.height ?? 0, current.height ?? 0, t),
|
|
4052
|
+
};
|
|
4053
|
+
}
|
|
4054
|
+
return { ...(positions[positions.length - 1] ?? positions[0] ?? { longitude: 0, latitude: 0 }) };
|
|
4055
|
+
}
|
|
4056
|
+
function uniqueSortedDistances(distances) {
|
|
4057
|
+
const epsilon = 1e-6;
|
|
4058
|
+
return [...distances].sort((first, second) => first - second).filter((distance, index, sorted) => {
|
|
4059
|
+
const previous = sorted[index - 1];
|
|
4060
|
+
return previous === undefined || Math.abs(distance - previous) > epsilon;
|
|
4061
|
+
});
|
|
4062
|
+
}
|
|
4063
|
+
function getAnimationSeconds() {
|
|
4064
|
+
if (typeof performance !== 'undefined')
|
|
4065
|
+
return performance.now() / 1000;
|
|
4066
|
+
return Date.now() / 1000;
|
|
4067
|
+
}
|
|
4068
|
+
function fract(value) {
|
|
4069
|
+
return value - Math.floor(value);
|
|
4070
|
+
}
|
|
4071
|
+
function lerp(start, end, amount) {
|
|
4072
|
+
return start + (end - start) * amount;
|
|
4073
|
+
}
|
|
4074
|
+
function roundWeight(value) {
|
|
4075
|
+
return Math.round(value * 100) / 100;
|
|
4076
|
+
}
|
|
4077
|
+
function finiteOr(value, fallback) {
|
|
4078
|
+
return Number.isFinite(value) ? value : fallback;
|
|
4079
|
+
}
|
|
4080
|
+
function clamp(value, min, max) {
|
|
4081
|
+
return Math.max(min, Math.min(max, value));
|
|
4082
|
+
}
|
|
4083
|
+
function clampInteger(value, min, max) {
|
|
4084
|
+
return Math.max(min, Math.min(max, Math.round(value)));
|
|
4085
|
+
}
|
|
4086
|
+
function clamp01(value) {
|
|
4087
|
+
return Math.max(0, Math.min(1, value));
|
|
4088
|
+
}
|
|
4089
|
+
//# sourceMappingURL=index.js.map
|