@deck.gl/test-utils 9.4.0-alpha.1 → 9.4.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +9811 -35
- package/dist/index.cjs.map +3 -4
- package/dist/vitest.cjs +4360 -23
- package/dist/vitest.cjs.map +3 -4
- package/package.json +10 -6
package/dist/vitest.cjs
CHANGED
|
@@ -15,7 +15,7 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
15
|
}
|
|
16
16
|
return to;
|
|
17
17
|
};
|
|
18
|
-
var __toCommonJS = (
|
|
18
|
+
var __toCommonJS = (mod2) => __copyProps(__defProp({}, "__esModule", { value: true }), mod2);
|
|
19
19
|
|
|
20
20
|
// dist/vitest.js
|
|
21
21
|
var vitest_exports = {};
|
|
@@ -33,8 +33,4346 @@ __export(vitest_exports, {
|
|
|
33
33
|
module.exports = __toCommonJS(vitest_exports);
|
|
34
34
|
var import_vitest = require("vitest");
|
|
35
35
|
|
|
36
|
-
//
|
|
37
|
-
var
|
|
36
|
+
// ../core/src/utils/log.ts
|
|
37
|
+
var import_log = require("@probe.gl/log");
|
|
38
|
+
var defaultLogger = new import_log.Log({ id: "deck" });
|
|
39
|
+
var log_default = defaultLogger;
|
|
40
|
+
|
|
41
|
+
// ../core/src/debug/loggers.ts
|
|
42
|
+
var logState = {
|
|
43
|
+
attributeUpdateStart: -1,
|
|
44
|
+
attributeManagerUpdateStart: -1,
|
|
45
|
+
attributeUpdateMessages: []
|
|
46
|
+
};
|
|
47
|
+
var LOG_LEVEL_MAJOR_UPDATE = 1;
|
|
48
|
+
var LOG_LEVEL_MINOR_UPDATE = 2;
|
|
49
|
+
var LOG_LEVEL_UPDATE_DETAIL = 3;
|
|
50
|
+
var LOG_LEVEL_INFO = 4;
|
|
51
|
+
var LOG_LEVEL_DRAW = 2;
|
|
52
|
+
var getLoggers = (log) => ({
|
|
53
|
+
/* Layer events */
|
|
54
|
+
"layer.changeFlag": (layer, key, flags) => {
|
|
55
|
+
log.log(LOG_LEVEL_UPDATE_DETAIL, `${layer.id} ${key}: `, flags[key])();
|
|
56
|
+
},
|
|
57
|
+
"layer.initialize": (layer) => {
|
|
58
|
+
log.log(LOG_LEVEL_MAJOR_UPDATE, `Initializing ${layer}`)();
|
|
59
|
+
},
|
|
60
|
+
"layer.update": (layer, needsUpdate) => {
|
|
61
|
+
if (needsUpdate) {
|
|
62
|
+
const flags = layer.getChangeFlags();
|
|
63
|
+
log.log(
|
|
64
|
+
LOG_LEVEL_MINOR_UPDATE,
|
|
65
|
+
`Updating ${layer} because: ${Object.keys(flags).filter((key) => flags[key]).join(", ")}`
|
|
66
|
+
)();
|
|
67
|
+
} else {
|
|
68
|
+
log.log(LOG_LEVEL_INFO, `${layer} does not need update`)();
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"layer.matched": (layer, changed) => {
|
|
72
|
+
if (changed) {
|
|
73
|
+
log.log(LOG_LEVEL_INFO, `Matched ${layer}, state transfered`)();
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
"layer.finalize": (layer) => {
|
|
77
|
+
log.log(LOG_LEVEL_MAJOR_UPDATE, `Finalizing ${layer}`)();
|
|
78
|
+
},
|
|
79
|
+
/* CompositeLayer events */
|
|
80
|
+
"compositeLayer.renderLayers": (layer, updated, subLayers) => {
|
|
81
|
+
if (updated) {
|
|
82
|
+
log.log(
|
|
83
|
+
LOG_LEVEL_MINOR_UPDATE,
|
|
84
|
+
`Composite layer rendered new subLayers ${layer}`,
|
|
85
|
+
subLayers
|
|
86
|
+
)();
|
|
87
|
+
} else {
|
|
88
|
+
log.log(LOG_LEVEL_INFO, `Composite layer reused subLayers ${layer}`, subLayers)();
|
|
89
|
+
}
|
|
90
|
+
},
|
|
91
|
+
/* LayerManager events */
|
|
92
|
+
"layerManager.setLayers": (layerManager, updated, layers) => {
|
|
93
|
+
if (updated) {
|
|
94
|
+
log.log(LOG_LEVEL_MINOR_UPDATE, `Updating ${layers.length} deck layers`)();
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
"layerManager.activateViewport": (layerManager, viewport) => {
|
|
98
|
+
log.log(LOG_LEVEL_UPDATE_DETAIL, "Viewport changed", viewport)();
|
|
99
|
+
},
|
|
100
|
+
/* AttributeManager events */
|
|
101
|
+
"attributeManager.invalidate": (attributeManager, trigger, attributeNames) => {
|
|
102
|
+
log.log(
|
|
103
|
+
LOG_LEVEL_MAJOR_UPDATE,
|
|
104
|
+
attributeNames ? `invalidated attributes ${attributeNames} (${trigger}) for ${attributeManager.id}` : `invalidated all attributes for ${attributeManager.id}`
|
|
105
|
+
)();
|
|
106
|
+
},
|
|
107
|
+
"attributeManager.updateStart": (attributeManager) => {
|
|
108
|
+
logState.attributeUpdateMessages.length = 0;
|
|
109
|
+
logState.attributeManagerUpdateStart = Date.now();
|
|
110
|
+
},
|
|
111
|
+
"attributeManager.updateEnd": (attributeManager, numInstances) => {
|
|
112
|
+
const timeMs = Math.round(Date.now() - logState.attributeManagerUpdateStart);
|
|
113
|
+
log.groupCollapsed(
|
|
114
|
+
LOG_LEVEL_MINOR_UPDATE,
|
|
115
|
+
`Updated attributes for ${numInstances} instances in ${attributeManager.id} in ${timeMs}ms`
|
|
116
|
+
)();
|
|
117
|
+
for (const updateMessage of logState.attributeUpdateMessages) {
|
|
118
|
+
log.log(LOG_LEVEL_UPDATE_DETAIL, updateMessage)();
|
|
119
|
+
}
|
|
120
|
+
log.groupEnd(LOG_LEVEL_MINOR_UPDATE)();
|
|
121
|
+
},
|
|
122
|
+
/* Attribute events */
|
|
123
|
+
"attribute.updateStart": (attribute) => {
|
|
124
|
+
logState.attributeUpdateStart = Date.now();
|
|
125
|
+
},
|
|
126
|
+
"attribute.allocate": (attribute, numInstances) => {
|
|
127
|
+
const message = `${attribute.id} allocated ${numInstances}`;
|
|
128
|
+
logState.attributeUpdateMessages.push(message);
|
|
129
|
+
},
|
|
130
|
+
"attribute.updateEnd": (attribute, numInstances) => {
|
|
131
|
+
const timeMs = Math.round(Date.now() - logState.attributeUpdateStart);
|
|
132
|
+
const message = `${attribute.id} updated ${numInstances} in ${timeMs}ms`;
|
|
133
|
+
logState.attributeUpdateMessages.push(message);
|
|
134
|
+
},
|
|
135
|
+
/* Render events */
|
|
136
|
+
"deckRenderer.renderLayers": (deckRenderer, renderStats, opts) => {
|
|
137
|
+
const { pass, redrawReason } = opts;
|
|
138
|
+
for (const status of renderStats) {
|
|
139
|
+
const { totalCount, visibleCount, compositeCount, pickableCount } = status;
|
|
140
|
+
const primitiveCount = totalCount - compositeCount;
|
|
141
|
+
const hiddenCount = primitiveCount - visibleCount;
|
|
142
|
+
log.log(
|
|
143
|
+
LOG_LEVEL_DRAW,
|
|
144
|
+
`RENDER #${deckRenderer.renderCount} ${visibleCount} (of ${totalCount} layers) to ${pass} because ${redrawReason} (${hiddenCount} hidden, ${compositeCount} composite ${pickableCount} pickable)`
|
|
145
|
+
)();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// ../core/src/debug/index.ts
|
|
151
|
+
var loggers = {};
|
|
152
|
+
if (true) {
|
|
153
|
+
loggers = getLoggers(log_default);
|
|
154
|
+
}
|
|
155
|
+
function debug(eventType, arg1, arg2, arg3) {
|
|
156
|
+
if (log_default.level > 0 && loggers[eventType]) {
|
|
157
|
+
loggers[eventType].call(null, arg1, arg2, arg3);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ../core/src/shaderlib/index.ts
|
|
162
|
+
var import_shadertools = require("@luma.gl/shadertools");
|
|
163
|
+
|
|
164
|
+
// ../core/src/shaderlib/misc/layer-uniforms.ts
|
|
165
|
+
var uniformBlockWGSL = (
|
|
166
|
+
/* wgsl */
|
|
167
|
+
`struct LayerUniforms {
|
|
168
|
+
opacity: f32,
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
@group(0) @binding(auto)
|
|
172
|
+
var<uniform> layer: LayerUniforms;
|
|
173
|
+
`
|
|
174
|
+
);
|
|
175
|
+
var uniformBlock = `layout(std140) uniform layerUniforms {
|
|
176
|
+
uniform float opacity;
|
|
177
|
+
} layer;
|
|
178
|
+
`;
|
|
179
|
+
var layerUniforms = {
|
|
180
|
+
name: "layer",
|
|
181
|
+
source: uniformBlockWGSL,
|
|
182
|
+
vs: uniformBlock,
|
|
183
|
+
fs: uniformBlock,
|
|
184
|
+
getUniforms: (props) => {
|
|
185
|
+
return {
|
|
186
|
+
// apply gamma to opacity to make it visually "linear"
|
|
187
|
+
// TODO - v10: use raw opacity?
|
|
188
|
+
opacity: Math.pow(props.opacity, 1 / 2.2)
|
|
189
|
+
};
|
|
190
|
+
},
|
|
191
|
+
uniformTypes: {
|
|
192
|
+
opacity: "f32"
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
// ../core/src/shaderlib/misc/geometry.ts
|
|
197
|
+
var source = (
|
|
198
|
+
/* wgsl */
|
|
199
|
+
`const SMOOTH_EDGE_RADIUS: f32 = 0.5;
|
|
200
|
+
|
|
201
|
+
struct VertexGeometry {
|
|
202
|
+
position: vec4<f32>,
|
|
203
|
+
worldPosition: vec3<f32>,
|
|
204
|
+
worldPositionAlt: vec3<f32>,
|
|
205
|
+
normal: vec3<f32>,
|
|
206
|
+
uv: vec2<f32>,
|
|
207
|
+
pickingColor: vec3<f32>,
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
var<private> geometry_: VertexGeometry = VertexGeometry(
|
|
211
|
+
vec4<f32>(0.0, 0.0, 1.0, 0.0),
|
|
212
|
+
vec3<f32>(0.0, 0.0, 0.0),
|
|
213
|
+
vec3<f32>(0.0, 0.0, 0.0),
|
|
214
|
+
vec3<f32>(0.0, 0.0, 0.0),
|
|
215
|
+
vec2<f32>(0.0, 0.0),
|
|
216
|
+
vec3<f32>(0.0, 0.0, 0.0)
|
|
217
|
+
);
|
|
218
|
+
|
|
219
|
+
struct FragmentGeometry {
|
|
220
|
+
uv: vec2<f32>,
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
var<private> fragmentGeometry: FragmentGeometry;
|
|
224
|
+
|
|
225
|
+
fn smoothedge(edge: f32, x: f32) -> f32 {
|
|
226
|
+
return smoothstep(edge - SMOOTH_EDGE_RADIUS, edge + SMOOTH_EDGE_RADIUS, x);
|
|
227
|
+
}
|
|
228
|
+
`
|
|
229
|
+
);
|
|
230
|
+
var defines = "#define SMOOTH_EDGE_RADIUS 0.5";
|
|
231
|
+
var vs = (
|
|
232
|
+
/* glsl */
|
|
233
|
+
`${defines}
|
|
234
|
+
|
|
235
|
+
struct VertexGeometry {
|
|
236
|
+
vec4 position;
|
|
237
|
+
vec3 worldPosition;
|
|
238
|
+
vec3 worldPositionAlt;
|
|
239
|
+
vec3 normal;
|
|
240
|
+
vec2 uv;
|
|
241
|
+
vec3 pickingColor;
|
|
242
|
+
} geometry = VertexGeometry(
|
|
243
|
+
vec4(0.0, 0.0, 1.0, 0.0),
|
|
244
|
+
vec3(0.0),
|
|
245
|
+
vec3(0.0),
|
|
246
|
+
vec3(0.0),
|
|
247
|
+
vec2(0.0),
|
|
248
|
+
vec3(0.0)
|
|
249
|
+
);
|
|
250
|
+
`
|
|
251
|
+
);
|
|
252
|
+
var fs = (
|
|
253
|
+
/* glsl */
|
|
254
|
+
`${defines}
|
|
255
|
+
|
|
256
|
+
struct FragmentGeometry {
|
|
257
|
+
vec2 uv;
|
|
258
|
+
};
|
|
259
|
+
FragmentGeometry geometry;
|
|
260
|
+
|
|
261
|
+
float smoothedge(float edge, float x) {
|
|
262
|
+
return smoothstep(edge - SMOOTH_EDGE_RADIUS, edge + SMOOTH_EDGE_RADIUS, x);
|
|
263
|
+
}
|
|
264
|
+
`
|
|
265
|
+
);
|
|
266
|
+
var geometry_default = {
|
|
267
|
+
name: "geometry",
|
|
268
|
+
source,
|
|
269
|
+
vs,
|
|
270
|
+
fs
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
// ../core/src/lib/constants.ts
|
|
274
|
+
var import_mjolnir = require("mjolnir.js");
|
|
275
|
+
var COORDINATE_SYSTEM = {
|
|
276
|
+
/**
|
|
277
|
+
* `LNGLAT` if rendering into a geospatial viewport, `CARTESIAN` otherwise
|
|
278
|
+
*/
|
|
279
|
+
DEFAULT: "default",
|
|
280
|
+
/**
|
|
281
|
+
* Positions are interpreted as [longitude, latitude, elevation]
|
|
282
|
+
* longitude/latitude are in degrees, elevation is in meters.
|
|
283
|
+
* Dimensions are in meters.
|
|
284
|
+
*/
|
|
285
|
+
LNGLAT: "lnglat",
|
|
286
|
+
/**
|
|
287
|
+
* Positions are interpreted as [x, y, z] in meter offsets from the coordinate origin.
|
|
288
|
+
* Dimensions are in meters.
|
|
289
|
+
*/
|
|
290
|
+
METER_OFFSETS: "meter-offsets",
|
|
291
|
+
/**
|
|
292
|
+
* Positions are interpreted as [deltaLng, deltaLat, elevation] from the coordinate origin.
|
|
293
|
+
* deltaLng/deltaLat are in degrees, elevation is in meters.
|
|
294
|
+
* Dimensions are in meters.
|
|
295
|
+
*/
|
|
296
|
+
LNGLAT_OFFSETS: "lnglat-offsets",
|
|
297
|
+
/**
|
|
298
|
+
* Positions and dimensions are in the common units of the viewport.
|
|
299
|
+
*/
|
|
300
|
+
CARTESIAN: "cartesian"
|
|
301
|
+
};
|
|
302
|
+
Object.defineProperty(COORDINATE_SYSTEM, "IDENTITY", {
|
|
303
|
+
get: () => {
|
|
304
|
+
log_default.deprecated("COORDINATE_SYSTEM.IDENTITY", "COORDINATE_SYSTEM.CARTESIAN")();
|
|
305
|
+
return COORDINATE_SYSTEM.CARTESIAN;
|
|
306
|
+
}
|
|
307
|
+
});
|
|
308
|
+
var PROJECTION_MODE = {
|
|
309
|
+
/**
|
|
310
|
+
* Render geospatial data in Web Mercator projection
|
|
311
|
+
*/
|
|
312
|
+
WEB_MERCATOR: 1,
|
|
313
|
+
/**
|
|
314
|
+
* Render geospatial data as a 3D globe
|
|
315
|
+
*/
|
|
316
|
+
GLOBE: 2,
|
|
317
|
+
/**
|
|
318
|
+
* (Internal use only) Web Mercator projection at high zoom
|
|
319
|
+
*/
|
|
320
|
+
WEB_MERCATOR_AUTO_OFFSET: 4,
|
|
321
|
+
/**
|
|
322
|
+
* No transformation
|
|
323
|
+
*/
|
|
324
|
+
IDENTITY: 0
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
// ../core/src/shaderlib/index.ts
|
|
328
|
+
var DEFAULT_MODULES = [geometry_default];
|
|
329
|
+
var SHADER_HOOKS_GLSL = [
|
|
330
|
+
"vs:DECKGL_FILTER_SIZE(inout vec3 size, VertexGeometry geometry)",
|
|
331
|
+
"vs:DECKGL_FILTER_GL_POSITION(inout vec4 position, VertexGeometry geometry)",
|
|
332
|
+
"vs:DECKGL_FILTER_COLOR(inout vec4 color, VertexGeometry geometry)",
|
|
333
|
+
"fs:DECKGL_FILTER_COLOR(inout vec4 color, FragmentGeometry geometry)"
|
|
334
|
+
];
|
|
335
|
+
var SHADER_HOOKS_WGSL = [
|
|
336
|
+
// Not yet supported
|
|
337
|
+
];
|
|
338
|
+
function getShaderAssembler(language) {
|
|
339
|
+
const shaderAssembler = import_shadertools.ShaderAssembler.getDefaultShaderAssembler(language);
|
|
340
|
+
for (const shaderModule of DEFAULT_MODULES) {
|
|
341
|
+
shaderAssembler.addDefaultModule(shaderModule);
|
|
342
|
+
}
|
|
343
|
+
shaderAssembler._hookFunctions.length = 0;
|
|
344
|
+
const shaderHooks = language === "glsl" ? SHADER_HOOKS_GLSL : SHADER_HOOKS_WGSL;
|
|
345
|
+
for (const shaderHook of shaderHooks) {
|
|
346
|
+
shaderAssembler.addShaderHook(shaderHook);
|
|
347
|
+
}
|
|
348
|
+
return shaderAssembler;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
// ../core/src/passes/pass.ts
|
|
352
|
+
var Pass = class {
|
|
353
|
+
/** Create a new Pass instance */
|
|
354
|
+
constructor(device2, props = { id: "pass" }) {
|
|
355
|
+
const { id } = props;
|
|
356
|
+
this.id = id;
|
|
357
|
+
this.device = device2;
|
|
358
|
+
this.props = { ...props };
|
|
359
|
+
}
|
|
360
|
+
setProps(props) {
|
|
361
|
+
Object.assign(this.props, props);
|
|
362
|
+
}
|
|
363
|
+
render(params) {
|
|
364
|
+
}
|
|
365
|
+
// eslint-disable-line @typescript-eslint/no-empty-function
|
|
366
|
+
cleanup() {
|
|
367
|
+
}
|
|
368
|
+
// eslint-disable-line @typescript-eslint/no-empty-function
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
// ../core/src/passes/layers-pass.ts
|
|
372
|
+
var WEBGPU_DEFAULT_DRAW_PARAMETERS = {
|
|
373
|
+
depthWriteEnabled: true,
|
|
374
|
+
depthCompare: "less-equal",
|
|
375
|
+
blendColorOperation: "add",
|
|
376
|
+
blendColorSrcFactor: "one",
|
|
377
|
+
blendColorDstFactor: "one-minus-src-alpha",
|
|
378
|
+
blendAlphaOperation: "add",
|
|
379
|
+
blendAlphaSrcFactor: "one",
|
|
380
|
+
blendAlphaDstFactor: "one-minus-src-alpha"
|
|
381
|
+
};
|
|
382
|
+
var LayersPass = class extends Pass {
|
|
383
|
+
constructor() {
|
|
384
|
+
super(...arguments);
|
|
385
|
+
this._lastRenderIndex = -1;
|
|
386
|
+
}
|
|
387
|
+
render(options) {
|
|
388
|
+
this._render(options);
|
|
389
|
+
}
|
|
390
|
+
_render(options) {
|
|
391
|
+
const { canvasContext = this.device.canvasContext } = options;
|
|
392
|
+
const framebuffer = options.target ?? canvasContext.getCurrentFramebuffer();
|
|
393
|
+
const [width, height] = canvasContext.getDrawingBufferSize();
|
|
394
|
+
const clearCanvas = options.clearCanvas ?? true;
|
|
395
|
+
let clearColor = options.clearColor ?? (clearCanvas ? [0, 0, 0, 0] : false);
|
|
396
|
+
let clearDepth = clearCanvas ? 1 : false;
|
|
397
|
+
let clearStencil = clearCanvas ? 0 : false;
|
|
398
|
+
const colorMask = options.colorMask ?? 15;
|
|
399
|
+
const parameters = { viewport: [0, 0, width, height] };
|
|
400
|
+
if (options.colorMask) {
|
|
401
|
+
parameters.colorMask = colorMask;
|
|
402
|
+
}
|
|
403
|
+
if (options.scissorRect) {
|
|
404
|
+
parameters.scissorRect = options.scissorRect;
|
|
405
|
+
}
|
|
406
|
+
const { shaderModuleProps, viewports, views, onViewportActive, clearStack = true } = options;
|
|
407
|
+
const pass = options.pass || "unknown";
|
|
408
|
+
const submitEachRenderPass = this.device.type === "webgpu";
|
|
409
|
+
if (clearStack) {
|
|
410
|
+
this._lastRenderIndex = -1;
|
|
411
|
+
}
|
|
412
|
+
const renderStats = [];
|
|
413
|
+
if (!viewports.length) {
|
|
414
|
+
const renderPass = this.device.beginRenderPass({
|
|
415
|
+
framebuffer,
|
|
416
|
+
parameters,
|
|
417
|
+
clearColor,
|
|
418
|
+
clearDepth,
|
|
419
|
+
clearStencil
|
|
420
|
+
});
|
|
421
|
+
renderPass.end();
|
|
422
|
+
this.device.submit();
|
|
423
|
+
return renderStats;
|
|
424
|
+
}
|
|
425
|
+
try {
|
|
426
|
+
for (const viewport of viewports) {
|
|
427
|
+
onViewportActive == null ? void 0 : onViewportActive(viewport);
|
|
428
|
+
const drawLayerParams = this._getDrawLayerParams(viewport, options);
|
|
429
|
+
const view = views && views[viewport.id];
|
|
430
|
+
const subViewports = viewport.subViewports || [viewport];
|
|
431
|
+
const renderGroups = submitEachRenderPass ? subViewports.map((subViewport) => [subViewport]) : [subViewports];
|
|
432
|
+
for (const renderGroup of renderGroups) {
|
|
433
|
+
const renderPass = this.device.beginRenderPass({
|
|
434
|
+
framebuffer,
|
|
435
|
+
parameters,
|
|
436
|
+
clearColor,
|
|
437
|
+
clearDepth,
|
|
438
|
+
clearStencil
|
|
439
|
+
});
|
|
440
|
+
try {
|
|
441
|
+
for (const subViewport of renderGroup) {
|
|
442
|
+
const stats = this._drawLayersInViewport(
|
|
443
|
+
renderPass,
|
|
444
|
+
{
|
|
445
|
+
target: framebuffer,
|
|
446
|
+
canvasContext,
|
|
447
|
+
shaderModuleProps,
|
|
448
|
+
viewport: subViewport,
|
|
449
|
+
view,
|
|
450
|
+
pass,
|
|
451
|
+
layers: options.layers,
|
|
452
|
+
isPicking: options.isPicking
|
|
453
|
+
},
|
|
454
|
+
drawLayerParams
|
|
455
|
+
);
|
|
456
|
+
renderStats.push(stats);
|
|
457
|
+
}
|
|
458
|
+
} finally {
|
|
459
|
+
renderPass.end();
|
|
460
|
+
if (submitEachRenderPass) {
|
|
461
|
+
this.device.submit();
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
clearColor = false;
|
|
465
|
+
clearDepth = false;
|
|
466
|
+
clearStencil = false;
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
return renderStats;
|
|
470
|
+
} finally {
|
|
471
|
+
if (!submitEachRenderPass) {
|
|
472
|
+
this.device.submit();
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
// When a viewport contains multiple subviewports (e.g. repeated web mercator map),
|
|
477
|
+
// this is only done once for the parent viewport
|
|
478
|
+
/* Resolve the parameters needed to draw each layer */
|
|
479
|
+
_getDrawLayerParams(viewport, {
|
|
480
|
+
layers,
|
|
481
|
+
pass,
|
|
482
|
+
isPicking = false,
|
|
483
|
+
layerFilter,
|
|
484
|
+
cullRect,
|
|
485
|
+
views,
|
|
486
|
+
effects,
|
|
487
|
+
canvasContext = this.device.canvasContext,
|
|
488
|
+
shaderModuleProps
|
|
489
|
+
}, evaluateShouldDrawOnly = false) {
|
|
490
|
+
var _a2, _b;
|
|
491
|
+
const drawLayerParams = [];
|
|
492
|
+
const indexResolver = layerIndexResolver(this._lastRenderIndex + 1);
|
|
493
|
+
const drawContext = {
|
|
494
|
+
layer: layers[0],
|
|
495
|
+
viewport,
|
|
496
|
+
isPicking,
|
|
497
|
+
renderPass: pass,
|
|
498
|
+
cullRect
|
|
499
|
+
};
|
|
500
|
+
const layerFilterCache = {};
|
|
501
|
+
for (let layerIndex = 0; layerIndex < layers.length; layerIndex++) {
|
|
502
|
+
const layer = layers[layerIndex];
|
|
503
|
+
const shouldDrawLayer = this._shouldDrawLayer(
|
|
504
|
+
layer,
|
|
505
|
+
drawContext,
|
|
506
|
+
layerFilter,
|
|
507
|
+
layerFilterCache
|
|
508
|
+
);
|
|
509
|
+
const layerParam = { shouldDrawLayer };
|
|
510
|
+
if (shouldDrawLayer && !evaluateShouldDrawOnly) {
|
|
511
|
+
layerParam.shouldDrawLayer = true;
|
|
512
|
+
layerParam.layerRenderIndex = indexResolver(layer, shouldDrawLayer);
|
|
513
|
+
layerParam.shaderModuleProps = this._getShaderModuleProps(
|
|
514
|
+
layer,
|
|
515
|
+
effects,
|
|
516
|
+
pass,
|
|
517
|
+
canvasContext,
|
|
518
|
+
shaderModuleProps
|
|
519
|
+
);
|
|
520
|
+
const defaultParams = layer.context.device.type === "webgpu" ? WEBGPU_DEFAULT_DRAW_PARAMETERS : null;
|
|
521
|
+
layerParam.layerParameters = {
|
|
522
|
+
...defaultParams,
|
|
523
|
+
...(_a2 = layer.context.deck) == null ? void 0 : _a2.props.parameters,
|
|
524
|
+
...(_b = views == null ? void 0 : views[viewport.id]) == null ? void 0 : _b.props.parameters,
|
|
525
|
+
...this.getLayerParameters(layer, layerIndex, viewport)
|
|
526
|
+
};
|
|
527
|
+
}
|
|
528
|
+
drawLayerParams[layerIndex] = layerParam;
|
|
529
|
+
}
|
|
530
|
+
return drawLayerParams;
|
|
531
|
+
}
|
|
532
|
+
// Draws a list of layers in one viewport
|
|
533
|
+
// TODO - when picking we could completely skip rendering viewports that dont
|
|
534
|
+
// intersect with the picking rect
|
|
535
|
+
/* eslint-disable max-depth, max-statements, complexity */
|
|
536
|
+
_drawLayersInViewport(renderPass, {
|
|
537
|
+
layers,
|
|
538
|
+
shaderModuleProps: globalModuleParameters,
|
|
539
|
+
pass,
|
|
540
|
+
target,
|
|
541
|
+
canvasContext,
|
|
542
|
+
viewport,
|
|
543
|
+
view,
|
|
544
|
+
isPicking
|
|
545
|
+
}, drawLayerParams) {
|
|
546
|
+
const glViewport = getGLViewport(this.device, {
|
|
547
|
+
canvasContext,
|
|
548
|
+
shaderModuleProps: globalModuleParameters,
|
|
549
|
+
target,
|
|
550
|
+
viewport
|
|
551
|
+
});
|
|
552
|
+
if (view) {
|
|
553
|
+
const { clear, clearColor, clearDepth, clearStencil } = view.props;
|
|
554
|
+
if (clear) {
|
|
555
|
+
let colorToUse = [0, 0, 0, 0];
|
|
556
|
+
let depthToUse = 1;
|
|
557
|
+
let stencilToUse = 0;
|
|
558
|
+
if (Array.isArray(clearColor) && !isPicking) {
|
|
559
|
+
colorToUse = [...clearColor.slice(0, 3), clearColor[3] || 255].map(
|
|
560
|
+
(c) => c / 255
|
|
561
|
+
);
|
|
562
|
+
} else if (clearColor === false) {
|
|
563
|
+
colorToUse = false;
|
|
564
|
+
}
|
|
565
|
+
if (clearDepth !== void 0) {
|
|
566
|
+
depthToUse = clearDepth;
|
|
567
|
+
}
|
|
568
|
+
if (clearStencil !== void 0) {
|
|
569
|
+
stencilToUse = clearStencil;
|
|
570
|
+
}
|
|
571
|
+
const clearRenderPass = this.device.beginRenderPass({
|
|
572
|
+
framebuffer: target,
|
|
573
|
+
parameters: {
|
|
574
|
+
viewport: glViewport,
|
|
575
|
+
scissorRect: glViewport
|
|
576
|
+
},
|
|
577
|
+
clearColor: colorToUse,
|
|
578
|
+
clearDepth: depthToUse,
|
|
579
|
+
clearStencil: stencilToUse
|
|
580
|
+
});
|
|
581
|
+
clearRenderPass.end();
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
const renderStatus = {
|
|
585
|
+
totalCount: layers.length,
|
|
586
|
+
visibleCount: 0,
|
|
587
|
+
compositeCount: 0,
|
|
588
|
+
pickableCount: 0
|
|
589
|
+
};
|
|
590
|
+
renderPass.setParameters({ viewport: glViewport });
|
|
591
|
+
for (let layerIndex = 0; layerIndex < layers.length; layerIndex++) {
|
|
592
|
+
const layer = layers[layerIndex];
|
|
593
|
+
const drawLayerParameters = drawLayerParams[layerIndex];
|
|
594
|
+
const { shouldDrawLayer } = drawLayerParameters;
|
|
595
|
+
if (shouldDrawLayer && layer.props.pickable) {
|
|
596
|
+
renderStatus.pickableCount++;
|
|
597
|
+
}
|
|
598
|
+
if (layer.isComposite) {
|
|
599
|
+
renderStatus.compositeCount++;
|
|
600
|
+
}
|
|
601
|
+
if (layer.isDrawable && drawLayerParameters.shouldDrawLayer) {
|
|
602
|
+
const { layerRenderIndex, shaderModuleProps, layerParameters } = drawLayerParameters;
|
|
603
|
+
renderStatus.visibleCount++;
|
|
604
|
+
this._lastRenderIndex = Math.max(this._lastRenderIndex, layerRenderIndex);
|
|
605
|
+
if (shaderModuleProps.project) {
|
|
606
|
+
shaderModuleProps.project.viewport = viewport;
|
|
607
|
+
}
|
|
608
|
+
layer.context.renderPass = renderPass;
|
|
609
|
+
try {
|
|
610
|
+
layer._drawLayer({
|
|
611
|
+
renderPass,
|
|
612
|
+
shaderModuleProps,
|
|
613
|
+
uniforms: { layerIndex: layerRenderIndex },
|
|
614
|
+
parameters: layerParameters
|
|
615
|
+
});
|
|
616
|
+
} catch (err) {
|
|
617
|
+
layer.raiseError(err, `drawing ${layer} to ${pass}`);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
return renderStatus;
|
|
622
|
+
}
|
|
623
|
+
/* eslint-enable max-depth, max-statements */
|
|
624
|
+
/* Methods for subclass overrides */
|
|
625
|
+
shouldDrawLayer(layer) {
|
|
626
|
+
return true;
|
|
627
|
+
}
|
|
628
|
+
getShaderModuleProps(layer, effects, otherShaderModuleProps) {
|
|
629
|
+
return null;
|
|
630
|
+
}
|
|
631
|
+
getLayerParameters(layer, layerIndex, viewport) {
|
|
632
|
+
return layer.props.parameters;
|
|
633
|
+
}
|
|
634
|
+
/* Private */
|
|
635
|
+
_shouldDrawLayer(layer, drawContext, layerFilter, layerFilterCache) {
|
|
636
|
+
const shouldDrawLayer = layer.props.visible && this.shouldDrawLayer(layer);
|
|
637
|
+
if (!shouldDrawLayer) {
|
|
638
|
+
return false;
|
|
639
|
+
}
|
|
640
|
+
drawContext.layer = layer;
|
|
641
|
+
let parent = layer.parent;
|
|
642
|
+
while (parent) {
|
|
643
|
+
if (!parent.props.visible || !parent.filterSubLayer(drawContext)) {
|
|
644
|
+
return false;
|
|
645
|
+
}
|
|
646
|
+
drawContext.layer = parent;
|
|
647
|
+
parent = parent.parent;
|
|
648
|
+
}
|
|
649
|
+
if (layerFilter) {
|
|
650
|
+
const rootLayerId = drawContext.layer.id;
|
|
651
|
+
if (!(rootLayerId in layerFilterCache)) {
|
|
652
|
+
layerFilterCache[rootLayerId] = layerFilter(drawContext);
|
|
653
|
+
}
|
|
654
|
+
if (!layerFilterCache[rootLayerId]) {
|
|
655
|
+
return false;
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
layer.activateViewport(drawContext.viewport);
|
|
659
|
+
return true;
|
|
660
|
+
}
|
|
661
|
+
_getShaderModuleProps(layer, effects, pass, canvasContext, overrides) {
|
|
662
|
+
var _a2, _b;
|
|
663
|
+
const devicePixelRatio = canvasContext.cssToDeviceRatio();
|
|
664
|
+
const layerProps = ((_a2 = layer.internalState) == null ? void 0 : _a2.propsInTransition) || layer.props;
|
|
665
|
+
const shaderModuleProps = {
|
|
666
|
+
layer: layerProps,
|
|
667
|
+
picking: {
|
|
668
|
+
isActive: false
|
|
669
|
+
},
|
|
670
|
+
project: {
|
|
671
|
+
viewport: layer.context.viewport,
|
|
672
|
+
devicePixelRatio,
|
|
673
|
+
modelMatrix: layerProps.modelMatrix,
|
|
674
|
+
coordinateSystem: layerProps.coordinateSystem,
|
|
675
|
+
coordinateOrigin: layerProps.coordinateOrigin,
|
|
676
|
+
autoWrapLongitude: layer.wrapLongitude
|
|
677
|
+
}
|
|
678
|
+
};
|
|
679
|
+
if (effects) {
|
|
680
|
+
for (const effect of effects) {
|
|
681
|
+
mergeModuleParameters(
|
|
682
|
+
shaderModuleProps,
|
|
683
|
+
(_b = effect.getShaderModuleProps) == null ? void 0 : _b.call(effect, layer, shaderModuleProps)
|
|
684
|
+
);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
for (const module2 of layer.context.defaultShaderModules) {
|
|
688
|
+
if (!(module2.name in shaderModuleProps)) {
|
|
689
|
+
shaderModuleProps[module2.name] = {};
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
return mergeModuleParameters(
|
|
693
|
+
shaderModuleProps,
|
|
694
|
+
this.getShaderModuleProps(layer, effects, shaderModuleProps),
|
|
695
|
+
overrides
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
};
|
|
699
|
+
function layerIndexResolver(startIndex = 0, layerIndices = {}) {
|
|
700
|
+
const resolvers = {};
|
|
701
|
+
const resolveLayerIndex = (layer, isDrawn) => {
|
|
702
|
+
const indexOverride = layer.props._offset;
|
|
703
|
+
const layerId = layer.id;
|
|
704
|
+
const parentId = layer.parent && layer.parent.id;
|
|
705
|
+
let index;
|
|
706
|
+
if (parentId && !(parentId in layerIndices)) {
|
|
707
|
+
resolveLayerIndex(layer.parent, false);
|
|
708
|
+
}
|
|
709
|
+
if (parentId in resolvers) {
|
|
710
|
+
const resolver = resolvers[parentId] = resolvers[parentId] || layerIndexResolver(layerIndices[parentId], layerIndices);
|
|
711
|
+
index = resolver(layer, isDrawn);
|
|
712
|
+
resolvers[layerId] = resolver;
|
|
713
|
+
} else if (Number.isFinite(indexOverride)) {
|
|
714
|
+
index = indexOverride + (layerIndices[parentId] || 0);
|
|
715
|
+
resolvers[layerId] = null;
|
|
716
|
+
} else {
|
|
717
|
+
index = startIndex;
|
|
718
|
+
}
|
|
719
|
+
if (isDrawn && index >= startIndex) {
|
|
720
|
+
startIndex = index + 1;
|
|
721
|
+
}
|
|
722
|
+
layerIndices[layerId] = index;
|
|
723
|
+
return index;
|
|
724
|
+
};
|
|
725
|
+
return resolveLayerIndex;
|
|
726
|
+
}
|
|
727
|
+
function getGLViewport(device2, {
|
|
728
|
+
canvasContext = device2.canvasContext,
|
|
729
|
+
shaderModuleProps,
|
|
730
|
+
target,
|
|
731
|
+
viewport
|
|
732
|
+
}) {
|
|
733
|
+
var _a2;
|
|
734
|
+
const pixelRatio = ((_a2 = shaderModuleProps == null ? void 0 : shaderModuleProps.project) == null ? void 0 : _a2.devicePixelRatio) ?? canvasContext.cssToDeviceRatio();
|
|
735
|
+
const [, drawingBufferHeight] = canvasContext.getDrawingBufferSize();
|
|
736
|
+
const height = target ? target.height : drawingBufferHeight;
|
|
737
|
+
const dimensions = viewport;
|
|
738
|
+
return [
|
|
739
|
+
dimensions.x * pixelRatio,
|
|
740
|
+
height - (dimensions.y + dimensions.height) * pixelRatio,
|
|
741
|
+
dimensions.width * pixelRatio,
|
|
742
|
+
dimensions.height * pixelRatio
|
|
743
|
+
];
|
|
744
|
+
}
|
|
745
|
+
function mergeModuleParameters(target, ...sources) {
|
|
746
|
+
for (const source2 of sources) {
|
|
747
|
+
if (source2) {
|
|
748
|
+
for (const key in source2) {
|
|
749
|
+
if (target[key]) {
|
|
750
|
+
Object.assign(target[key], source2[key]);
|
|
751
|
+
} else {
|
|
752
|
+
target[key] = source2[key];
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
return target;
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// ../core/src/utils/math-utils.ts
|
|
761
|
+
var import_core = require("@math.gl/core");
|
|
762
|
+
function createMat4() {
|
|
763
|
+
return [1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1];
|
|
764
|
+
}
|
|
765
|
+
function mod(value, divisor) {
|
|
766
|
+
const modulus = value % divisor;
|
|
767
|
+
return modulus < 0 ? divisor + modulus : modulus;
|
|
768
|
+
}
|
|
769
|
+
function getCameraPosition(viewMatrixInverse) {
|
|
770
|
+
return [viewMatrixInverse[12], viewMatrixInverse[13], viewMatrixInverse[14]];
|
|
771
|
+
}
|
|
772
|
+
function getFrustumPlanes(viewProjectionMatrix) {
|
|
773
|
+
return {
|
|
774
|
+
left: getFrustumPlane(
|
|
775
|
+
viewProjectionMatrix[3] + viewProjectionMatrix[0],
|
|
776
|
+
viewProjectionMatrix[7] + viewProjectionMatrix[4],
|
|
777
|
+
viewProjectionMatrix[11] + viewProjectionMatrix[8],
|
|
778
|
+
viewProjectionMatrix[15] + viewProjectionMatrix[12]
|
|
779
|
+
),
|
|
780
|
+
right: getFrustumPlane(
|
|
781
|
+
viewProjectionMatrix[3] - viewProjectionMatrix[0],
|
|
782
|
+
viewProjectionMatrix[7] - viewProjectionMatrix[4],
|
|
783
|
+
viewProjectionMatrix[11] - viewProjectionMatrix[8],
|
|
784
|
+
viewProjectionMatrix[15] - viewProjectionMatrix[12]
|
|
785
|
+
),
|
|
786
|
+
bottom: getFrustumPlane(
|
|
787
|
+
viewProjectionMatrix[3] + viewProjectionMatrix[1],
|
|
788
|
+
viewProjectionMatrix[7] + viewProjectionMatrix[5],
|
|
789
|
+
viewProjectionMatrix[11] + viewProjectionMatrix[9],
|
|
790
|
+
viewProjectionMatrix[15] + viewProjectionMatrix[13]
|
|
791
|
+
),
|
|
792
|
+
top: getFrustumPlane(
|
|
793
|
+
viewProjectionMatrix[3] - viewProjectionMatrix[1],
|
|
794
|
+
viewProjectionMatrix[7] - viewProjectionMatrix[5],
|
|
795
|
+
viewProjectionMatrix[11] - viewProjectionMatrix[9],
|
|
796
|
+
viewProjectionMatrix[15] - viewProjectionMatrix[13]
|
|
797
|
+
),
|
|
798
|
+
near: getFrustumPlane(
|
|
799
|
+
viewProjectionMatrix[3] + viewProjectionMatrix[2],
|
|
800
|
+
viewProjectionMatrix[7] + viewProjectionMatrix[6],
|
|
801
|
+
viewProjectionMatrix[11] + viewProjectionMatrix[10],
|
|
802
|
+
viewProjectionMatrix[15] + viewProjectionMatrix[14]
|
|
803
|
+
),
|
|
804
|
+
far: getFrustumPlane(
|
|
805
|
+
viewProjectionMatrix[3] - viewProjectionMatrix[2],
|
|
806
|
+
viewProjectionMatrix[7] - viewProjectionMatrix[6],
|
|
807
|
+
viewProjectionMatrix[11] - viewProjectionMatrix[10],
|
|
808
|
+
viewProjectionMatrix[15] - viewProjectionMatrix[14]
|
|
809
|
+
)
|
|
810
|
+
};
|
|
811
|
+
}
|
|
812
|
+
var scratchVector = new import_core.Vector3();
|
|
813
|
+
function getFrustumPlane(a, b, c, d) {
|
|
814
|
+
scratchVector.set(a, b, c);
|
|
815
|
+
const L = scratchVector.len();
|
|
816
|
+
return { distance: d / L, normal: new import_core.Vector3(-a / L, -b / L, -c / L) };
|
|
817
|
+
}
|
|
818
|
+
|
|
819
|
+
// ../core/src/viewports/viewport.ts
|
|
820
|
+
var import_core2 = require("@math.gl/core");
|
|
821
|
+
var import_web_mercator = require("@math.gl/web-mercator");
|
|
822
|
+
var DEGREES_TO_RADIANS = Math.PI / 180;
|
|
823
|
+
var IDENTITY = createMat4();
|
|
824
|
+
var ZERO_VECTOR = [0, 0, 0];
|
|
825
|
+
var DEFAULT_DISTANCE_SCALES = {
|
|
826
|
+
unitsPerMeter: [1, 1, 1],
|
|
827
|
+
metersPerUnit: [1, 1, 1]
|
|
828
|
+
};
|
|
829
|
+
function createProjectionMatrix({
|
|
830
|
+
width,
|
|
831
|
+
height,
|
|
832
|
+
orthographic,
|
|
833
|
+
fovyRadians,
|
|
834
|
+
focalDistance,
|
|
835
|
+
padding,
|
|
836
|
+
near,
|
|
837
|
+
far
|
|
838
|
+
}) {
|
|
839
|
+
const aspect = width / height;
|
|
840
|
+
const matrix = orthographic ? new import_core2.Matrix4().orthographic({ fovy: fovyRadians, aspect, focalDistance, near, far }) : new import_core2.Matrix4().perspective({ fovy: fovyRadians, aspect, near, far });
|
|
841
|
+
if (padding) {
|
|
842
|
+
const { left = 0, right = 0, top = 0, bottom = 0 } = padding;
|
|
843
|
+
const offsetX = (0, import_core2.clamp)((left + width - right) / 2, 0, width) - width / 2;
|
|
844
|
+
const offsetY = (0, import_core2.clamp)((top + height - bottom) / 2, 0, height) - height / 2;
|
|
845
|
+
matrix[8] -= offsetX * 2 / width;
|
|
846
|
+
matrix[9] += offsetY * 2 / height;
|
|
847
|
+
}
|
|
848
|
+
return matrix;
|
|
849
|
+
}
|
|
850
|
+
var _Viewport = class _Viewport {
|
|
851
|
+
// eslint-disable-next-line complexity
|
|
852
|
+
constructor(opts = {}) {
|
|
853
|
+
this._frustumPlanes = {};
|
|
854
|
+
this.id = opts.id || this.constructor.displayName || "viewport";
|
|
855
|
+
this.x = opts.x || 0;
|
|
856
|
+
this.y = opts.y || 0;
|
|
857
|
+
this.width = opts.width || 1;
|
|
858
|
+
this.height = opts.height || 1;
|
|
859
|
+
this.zoom = opts.zoom || 0;
|
|
860
|
+
this.padding = opts.padding;
|
|
861
|
+
this.distanceScales = opts.distanceScales || DEFAULT_DISTANCE_SCALES;
|
|
862
|
+
this.focalDistance = opts.focalDistance || 1;
|
|
863
|
+
this.position = opts.position || ZERO_VECTOR;
|
|
864
|
+
this.modelMatrix = opts.modelMatrix || null;
|
|
865
|
+
const { longitude, latitude } = opts;
|
|
866
|
+
this.isGeospatial = Number.isFinite(latitude) && Number.isFinite(longitude);
|
|
867
|
+
this._initProps(opts);
|
|
868
|
+
this._initMatrices(opts);
|
|
869
|
+
this.equals = this.equals.bind(this);
|
|
870
|
+
this.project = this.project.bind(this);
|
|
871
|
+
this.unproject = this.unproject.bind(this);
|
|
872
|
+
this.projectPosition = this.projectPosition.bind(this);
|
|
873
|
+
this.unprojectPosition = this.unprojectPosition.bind(this);
|
|
874
|
+
this.projectFlat = this.projectFlat.bind(this);
|
|
875
|
+
this.unprojectFlat = this.unprojectFlat.bind(this);
|
|
876
|
+
}
|
|
877
|
+
get subViewports() {
|
|
878
|
+
return null;
|
|
879
|
+
}
|
|
880
|
+
get metersPerPixel() {
|
|
881
|
+
return this.distanceScales.metersPerUnit[2] / this.scale;
|
|
882
|
+
}
|
|
883
|
+
get projectionMode() {
|
|
884
|
+
if (this.isGeospatial) {
|
|
885
|
+
return this.zoom < 12 ? PROJECTION_MODE.WEB_MERCATOR : PROJECTION_MODE.WEB_MERCATOR_AUTO_OFFSET;
|
|
886
|
+
}
|
|
887
|
+
return PROJECTION_MODE.IDENTITY;
|
|
888
|
+
}
|
|
889
|
+
// Two viewports are equal if width and height are identical, and if
|
|
890
|
+
// their view and projection matrices are (approximately) equal.
|
|
891
|
+
equals(viewport) {
|
|
892
|
+
if (!(viewport instanceof _Viewport)) {
|
|
893
|
+
return false;
|
|
894
|
+
}
|
|
895
|
+
if (this === viewport) {
|
|
896
|
+
return true;
|
|
897
|
+
}
|
|
898
|
+
return viewport.width === this.width && viewport.height === this.height && viewport.scale === this.scale && (0, import_core2.equals)(viewport.projectionMatrix, this.projectionMatrix) && (0, import_core2.equals)(viewport.viewMatrix, this.viewMatrix);
|
|
899
|
+
}
|
|
900
|
+
/**
|
|
901
|
+
* Projects xyz (possibly latitude and longitude) to pixel coordinates in window
|
|
902
|
+
* using viewport projection parameters
|
|
903
|
+
* - [longitude, latitude] to [x, y]
|
|
904
|
+
* - [longitude, latitude, Z] => [x, y, z]
|
|
905
|
+
* Note: By default, returns top-left coordinates for canvas/SVG type render
|
|
906
|
+
*
|
|
907
|
+
* @param {Array} lngLatZ - [lng, lat] or [lng, lat, Z]
|
|
908
|
+
* @param {Object} opts - options
|
|
909
|
+
* @param {Object} opts.topLeft=true - Whether projected coords are top left
|
|
910
|
+
* @return {Array} - [x, y] or [x, y, z] in top left coords
|
|
911
|
+
*/
|
|
912
|
+
project(xyz, { topLeft = true } = {}) {
|
|
913
|
+
const worldPosition = this.projectPosition(xyz);
|
|
914
|
+
const coord = (0, import_web_mercator.worldToPixels)(worldPosition, this.pixelProjectionMatrix);
|
|
915
|
+
const [x, y] = coord;
|
|
916
|
+
const y2 = topLeft ? y : this.height - y;
|
|
917
|
+
return xyz.length === 2 ? [x, y2] : [x, y2, coord[2]];
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* Unproject pixel coordinates on screen onto world coordinates,
|
|
921
|
+
* (possibly [lon, lat]) on map.
|
|
922
|
+
* - [x, y] => [lng, lat]
|
|
923
|
+
* - [x, y, z] => [lng, lat, Z]
|
|
924
|
+
* @param {Array} xyz -
|
|
925
|
+
* @param {Object} opts - options
|
|
926
|
+
* @param {Object} opts.topLeft=true - Whether origin is top left
|
|
927
|
+
* @return {Array|null} - [lng, lat, Z] or [X, Y, Z]
|
|
928
|
+
*/
|
|
929
|
+
unproject(xyz, { topLeft = true, targetZ } = {}) {
|
|
930
|
+
const [x, y, z] = xyz;
|
|
931
|
+
const y2 = topLeft ? y : this.height - y;
|
|
932
|
+
const targetZWorld = targetZ && targetZ * this.distanceScales.unitsPerMeter[2];
|
|
933
|
+
const coord = (0, import_web_mercator.pixelsToWorld)([x, y2, z], this.pixelUnprojectionMatrix, targetZWorld);
|
|
934
|
+
const [X, Y, Z] = this.unprojectPosition(coord);
|
|
935
|
+
if (Number.isFinite(z)) {
|
|
936
|
+
return [X, Y, Z];
|
|
937
|
+
}
|
|
938
|
+
return Number.isFinite(targetZ) ? [X, Y, targetZ] : [X, Y];
|
|
939
|
+
}
|
|
940
|
+
// NON_LINEAR PROJECTION HOOKS
|
|
941
|
+
// Used for web meractor projection
|
|
942
|
+
projectPosition(xyz) {
|
|
943
|
+
const [X, Y] = this.projectFlat(xyz);
|
|
944
|
+
const Z = (xyz[2] || 0) * this.distanceScales.unitsPerMeter[2];
|
|
945
|
+
return [X, Y, Z];
|
|
946
|
+
}
|
|
947
|
+
unprojectPosition(xyz) {
|
|
948
|
+
const [X, Y] = this.unprojectFlat(xyz);
|
|
949
|
+
const Z = (xyz[2] || 0) * this.distanceScales.metersPerUnit[2];
|
|
950
|
+
return [X, Y, Z];
|
|
951
|
+
}
|
|
952
|
+
/**
|
|
953
|
+
* Project [lng,lat] on sphere onto [x,y] on 512*512 Mercator Zoom 0 tile.
|
|
954
|
+
* Performs the nonlinear part of the web mercator projection.
|
|
955
|
+
* Remaining projection is done with 4x4 matrices which also handles
|
|
956
|
+
* perspective.
|
|
957
|
+
* @param {Array} lngLat - [lng, lat] coordinates
|
|
958
|
+
* Specifies a point on the sphere to project onto the map.
|
|
959
|
+
* @return {Array} [x,y] coordinates.
|
|
960
|
+
*/
|
|
961
|
+
projectFlat(xyz) {
|
|
962
|
+
if (this.isGeospatial) {
|
|
963
|
+
const result = (0, import_web_mercator.lngLatToWorld)(xyz);
|
|
964
|
+
result[1] = (0, import_core2.clamp)(result[1], -318, 830);
|
|
965
|
+
return result;
|
|
966
|
+
}
|
|
967
|
+
return xyz;
|
|
968
|
+
}
|
|
969
|
+
/**
|
|
970
|
+
* Unproject world point [x,y] on map onto {lat, lon} on sphere
|
|
971
|
+
* @param {object|Vector} xy - object with {x,y} members
|
|
972
|
+
* representing point on projected map plane
|
|
973
|
+
* @return {GeoCoordinates} - object with {lat,lon} of point on sphere.
|
|
974
|
+
* Has toArray method if you need a GeoJSON Array.
|
|
975
|
+
* Per cartographic tradition, lat and lon are specified as degrees.
|
|
976
|
+
*/
|
|
977
|
+
unprojectFlat(xyz) {
|
|
978
|
+
if (this.isGeospatial) {
|
|
979
|
+
return (0, import_web_mercator.worldToLngLat)(xyz);
|
|
980
|
+
}
|
|
981
|
+
return xyz;
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* Get bounds of the current viewport
|
|
985
|
+
* @return {Array} - [minX, minY, maxX, maxY]
|
|
986
|
+
*/
|
|
987
|
+
getBounds(options = {}) {
|
|
988
|
+
const unprojectOption = { targetZ: options.z || 0 };
|
|
989
|
+
const topLeft = this.unproject([0, 0], unprojectOption);
|
|
990
|
+
const topRight = this.unproject([this.width, 0], unprojectOption);
|
|
991
|
+
const bottomLeft = this.unproject([0, this.height], unprojectOption);
|
|
992
|
+
const bottomRight = this.unproject([this.width, this.height], unprojectOption);
|
|
993
|
+
return [
|
|
994
|
+
Math.min(topLeft[0], topRight[0], bottomLeft[0], bottomRight[0]),
|
|
995
|
+
Math.min(topLeft[1], topRight[1], bottomLeft[1], bottomRight[1]),
|
|
996
|
+
Math.max(topLeft[0], topRight[0], bottomLeft[0], bottomRight[0]),
|
|
997
|
+
Math.max(topLeft[1], topRight[1], bottomLeft[1], bottomRight[1])
|
|
998
|
+
];
|
|
999
|
+
}
|
|
1000
|
+
getDistanceScales(coordinateOrigin) {
|
|
1001
|
+
if (coordinateOrigin && this.isGeospatial) {
|
|
1002
|
+
return (0, import_web_mercator.getDistanceScales)({
|
|
1003
|
+
longitude: coordinateOrigin[0],
|
|
1004
|
+
latitude: coordinateOrigin[1],
|
|
1005
|
+
highPrecision: true
|
|
1006
|
+
});
|
|
1007
|
+
}
|
|
1008
|
+
return this.distanceScales;
|
|
1009
|
+
}
|
|
1010
|
+
containsPixel({
|
|
1011
|
+
x,
|
|
1012
|
+
y,
|
|
1013
|
+
width = 1,
|
|
1014
|
+
height = 1
|
|
1015
|
+
}) {
|
|
1016
|
+
return x < this.x + this.width && this.x < x + width && y < this.y + this.height && this.y < y + height;
|
|
1017
|
+
}
|
|
1018
|
+
// Extract frustum planes in common space
|
|
1019
|
+
getFrustumPlanes() {
|
|
1020
|
+
if (this._frustumPlanes.near) {
|
|
1021
|
+
return this._frustumPlanes;
|
|
1022
|
+
}
|
|
1023
|
+
Object.assign(this._frustumPlanes, getFrustumPlanes(this.viewProjectionMatrix));
|
|
1024
|
+
return this._frustumPlanes;
|
|
1025
|
+
}
|
|
1026
|
+
// EXPERIMENTAL METHODS
|
|
1027
|
+
/**
|
|
1028
|
+
* Needed by panning and linear transition
|
|
1029
|
+
* Pan the viewport to place a given world coordinate at screen point [x, y]
|
|
1030
|
+
*
|
|
1031
|
+
* @param {Array} coords - world coordinates
|
|
1032
|
+
* @param {Array} pixel - [x,y] coordinates on screen
|
|
1033
|
+
* @param {Array} startPixel - [x,y] screen position where pan started (optional, for delta-based panning)
|
|
1034
|
+
* @return {Object} props of the new viewport
|
|
1035
|
+
*/
|
|
1036
|
+
panByPosition(coords, pixel, startPixel) {
|
|
1037
|
+
return null;
|
|
1038
|
+
}
|
|
1039
|
+
// INTERNAL METHODS
|
|
1040
|
+
/* eslint-disable complexity, max-statements */
|
|
1041
|
+
_initProps(opts) {
|
|
1042
|
+
const longitude = opts.longitude;
|
|
1043
|
+
const latitude = opts.latitude;
|
|
1044
|
+
if (this.isGeospatial) {
|
|
1045
|
+
if (!Number.isFinite(opts.zoom)) {
|
|
1046
|
+
this.zoom = (0, import_web_mercator.getMeterZoom)({ latitude }) + Math.log2(this.focalDistance);
|
|
1047
|
+
}
|
|
1048
|
+
this.distanceScales = opts.distanceScales || (0, import_web_mercator.getDistanceScales)({ latitude, longitude });
|
|
1049
|
+
}
|
|
1050
|
+
const scale = Math.pow(2, this.zoom);
|
|
1051
|
+
this.scale = scale;
|
|
1052
|
+
const { position, modelMatrix } = opts;
|
|
1053
|
+
let meterOffset = ZERO_VECTOR;
|
|
1054
|
+
if (position) {
|
|
1055
|
+
meterOffset = modelMatrix ? new import_core2.Matrix4(modelMatrix).transformAsVector(position, []) : position;
|
|
1056
|
+
}
|
|
1057
|
+
if (this.isGeospatial) {
|
|
1058
|
+
const center = this.projectPosition([longitude, latitude, 0]);
|
|
1059
|
+
this.center = new import_core2.Vector3(meterOffset).scale(this.distanceScales.unitsPerMeter).add(center);
|
|
1060
|
+
} else {
|
|
1061
|
+
this.center = this.projectPosition(meterOffset);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
/* eslint-enable complexity, max-statements */
|
|
1065
|
+
_initMatrices(opts) {
|
|
1066
|
+
const {
|
|
1067
|
+
// View matrix
|
|
1068
|
+
viewMatrix = IDENTITY,
|
|
1069
|
+
// Projection matrix
|
|
1070
|
+
projectionMatrix = null,
|
|
1071
|
+
// Projection matrix parameters, used if projectionMatrix not supplied
|
|
1072
|
+
orthographic = false,
|
|
1073
|
+
fovyRadians,
|
|
1074
|
+
fovy = 75,
|
|
1075
|
+
near = 0.1,
|
|
1076
|
+
// Distance of near clipping plane
|
|
1077
|
+
far = 1e3,
|
|
1078
|
+
// Distance of far clipping plane
|
|
1079
|
+
padding = null,
|
|
1080
|
+
// Center offset in pixels
|
|
1081
|
+
focalDistance = 1
|
|
1082
|
+
} = opts;
|
|
1083
|
+
this.viewMatrixUncentered = viewMatrix;
|
|
1084
|
+
this.viewMatrix = new import_core2.Matrix4().multiplyRight(viewMatrix).translate(new import_core2.Vector3(this.center).negate());
|
|
1085
|
+
this.projectionMatrix = projectionMatrix || createProjectionMatrix({
|
|
1086
|
+
width: this.width,
|
|
1087
|
+
height: this.height,
|
|
1088
|
+
orthographic,
|
|
1089
|
+
fovyRadians: fovyRadians || fovy * DEGREES_TO_RADIANS,
|
|
1090
|
+
focalDistance,
|
|
1091
|
+
padding,
|
|
1092
|
+
near,
|
|
1093
|
+
far
|
|
1094
|
+
});
|
|
1095
|
+
const vpm = createMat4();
|
|
1096
|
+
import_core2.mat4.multiply(vpm, vpm, this.projectionMatrix);
|
|
1097
|
+
import_core2.mat4.multiply(vpm, vpm, this.viewMatrix);
|
|
1098
|
+
this.viewProjectionMatrix = vpm;
|
|
1099
|
+
this.viewMatrixInverse = import_core2.mat4.invert([], this.viewMatrix) || this.viewMatrix;
|
|
1100
|
+
this.cameraPosition = getCameraPosition(this.viewMatrixInverse);
|
|
1101
|
+
const viewportMatrix = createMat4();
|
|
1102
|
+
const pixelProjectionMatrix = createMat4();
|
|
1103
|
+
import_core2.mat4.scale(viewportMatrix, viewportMatrix, [this.width / 2, -this.height / 2, 1]);
|
|
1104
|
+
import_core2.mat4.translate(viewportMatrix, viewportMatrix, [1, -1, 0]);
|
|
1105
|
+
import_core2.mat4.multiply(pixelProjectionMatrix, viewportMatrix, this.viewProjectionMatrix);
|
|
1106
|
+
this.pixelProjectionMatrix = pixelProjectionMatrix;
|
|
1107
|
+
this.pixelUnprojectionMatrix = import_core2.mat4.invert(createMat4(), this.pixelProjectionMatrix);
|
|
1108
|
+
if (!this.pixelUnprojectionMatrix) {
|
|
1109
|
+
log_default.warn("Pixel project matrix not invertible")();
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1112
|
+
};
|
|
1113
|
+
_Viewport.displayName = "Viewport";
|
|
1114
|
+
var Viewport = _Viewport;
|
|
1115
|
+
|
|
1116
|
+
// ../core/src/viewports/web-mercator-viewport.ts
|
|
1117
|
+
var import_web_mercator2 = require("@math.gl/web-mercator");
|
|
1118
|
+
var import_core3 = require("@math.gl/core");
|
|
1119
|
+
var _WebMercatorViewport = class _WebMercatorViewport extends Viewport {
|
|
1120
|
+
/* eslint-disable complexity, max-statements */
|
|
1121
|
+
constructor(opts = {}) {
|
|
1122
|
+
const {
|
|
1123
|
+
latitude = 0,
|
|
1124
|
+
longitude = 0,
|
|
1125
|
+
zoom = 0,
|
|
1126
|
+
pitch = 0,
|
|
1127
|
+
bearing = 0,
|
|
1128
|
+
nearZMultiplier = 0.1,
|
|
1129
|
+
farZMultiplier = 1.01,
|
|
1130
|
+
nearZ,
|
|
1131
|
+
farZ,
|
|
1132
|
+
orthographic = false,
|
|
1133
|
+
projectionMatrix,
|
|
1134
|
+
repeat = false,
|
|
1135
|
+
worldOffset = 0,
|
|
1136
|
+
position,
|
|
1137
|
+
padding,
|
|
1138
|
+
// backward compatibility
|
|
1139
|
+
// TODO: remove in v9
|
|
1140
|
+
legacyMeterSizes = false
|
|
1141
|
+
} = opts;
|
|
1142
|
+
let { width, height, altitude = 1.5 } = opts;
|
|
1143
|
+
const scale = Math.pow(2, zoom);
|
|
1144
|
+
width = width || 1;
|
|
1145
|
+
height = height || 1;
|
|
1146
|
+
let fovy;
|
|
1147
|
+
let projectionParameters = null;
|
|
1148
|
+
if (projectionMatrix) {
|
|
1149
|
+
altitude = projectionMatrix[5] / 2;
|
|
1150
|
+
fovy = (0, import_web_mercator2.altitudeToFovy)(altitude);
|
|
1151
|
+
} else {
|
|
1152
|
+
if (opts.fovy) {
|
|
1153
|
+
fovy = opts.fovy;
|
|
1154
|
+
altitude = (0, import_web_mercator2.fovyToAltitude)(fovy);
|
|
1155
|
+
} else {
|
|
1156
|
+
fovy = (0, import_web_mercator2.altitudeToFovy)(altitude);
|
|
1157
|
+
}
|
|
1158
|
+
let offset;
|
|
1159
|
+
if (padding) {
|
|
1160
|
+
const { top = 0, bottom = 0 } = padding;
|
|
1161
|
+
offset = [0, (0, import_core3.clamp)((top + height - bottom) / 2, 0, height) - height / 2];
|
|
1162
|
+
}
|
|
1163
|
+
projectionParameters = (0, import_web_mercator2.getProjectionParameters)({
|
|
1164
|
+
width,
|
|
1165
|
+
height,
|
|
1166
|
+
scale,
|
|
1167
|
+
center: position && [0, 0, position[2] * (0, import_web_mercator2.unitsPerMeter)(latitude)],
|
|
1168
|
+
offset,
|
|
1169
|
+
pitch,
|
|
1170
|
+
fovy,
|
|
1171
|
+
nearZMultiplier,
|
|
1172
|
+
farZMultiplier
|
|
1173
|
+
});
|
|
1174
|
+
if (Number.isFinite(nearZ)) {
|
|
1175
|
+
projectionParameters.near = nearZ;
|
|
1176
|
+
}
|
|
1177
|
+
if (Number.isFinite(farZ)) {
|
|
1178
|
+
projectionParameters.far = farZ;
|
|
1179
|
+
}
|
|
1180
|
+
}
|
|
1181
|
+
let viewMatrixUncentered = (0, import_web_mercator2.getViewMatrix)({
|
|
1182
|
+
height,
|
|
1183
|
+
pitch,
|
|
1184
|
+
bearing,
|
|
1185
|
+
scale,
|
|
1186
|
+
altitude
|
|
1187
|
+
});
|
|
1188
|
+
if (worldOffset) {
|
|
1189
|
+
const viewOffset = new import_core3.Matrix4().translate([512 * worldOffset, 0, 0]);
|
|
1190
|
+
viewMatrixUncentered = viewOffset.multiplyLeft(viewMatrixUncentered);
|
|
1191
|
+
}
|
|
1192
|
+
super({
|
|
1193
|
+
...opts,
|
|
1194
|
+
// x, y,
|
|
1195
|
+
width,
|
|
1196
|
+
height,
|
|
1197
|
+
// view matrix
|
|
1198
|
+
viewMatrix: viewMatrixUncentered,
|
|
1199
|
+
longitude,
|
|
1200
|
+
latitude,
|
|
1201
|
+
zoom,
|
|
1202
|
+
// projection matrix parameters
|
|
1203
|
+
...projectionParameters,
|
|
1204
|
+
fovy,
|
|
1205
|
+
focalDistance: altitude
|
|
1206
|
+
});
|
|
1207
|
+
this.latitude = latitude;
|
|
1208
|
+
this.longitude = longitude;
|
|
1209
|
+
this.zoom = zoom;
|
|
1210
|
+
this.pitch = pitch;
|
|
1211
|
+
this.bearing = bearing;
|
|
1212
|
+
this.altitude = altitude;
|
|
1213
|
+
this.fovy = fovy;
|
|
1214
|
+
this.orthographic = orthographic;
|
|
1215
|
+
this._subViewports = repeat ? [] : null;
|
|
1216
|
+
this._pseudoMeters = legacyMeterSizes;
|
|
1217
|
+
Object.freeze(this);
|
|
1218
|
+
}
|
|
1219
|
+
/* eslint-enable complexity, max-statements */
|
|
1220
|
+
get subViewports() {
|
|
1221
|
+
if (this._subViewports && !this._subViewports.length) {
|
|
1222
|
+
const bounds = this.getBounds();
|
|
1223
|
+
const minOffset = Math.floor((bounds[0] + 180) / 360);
|
|
1224
|
+
const maxOffset = Math.ceil((bounds[2] - 180) / 360);
|
|
1225
|
+
for (let x = minOffset; x <= maxOffset; x++) {
|
|
1226
|
+
const offsetViewport = x ? new _WebMercatorViewport({
|
|
1227
|
+
...this,
|
|
1228
|
+
worldOffset: x
|
|
1229
|
+
}) : this;
|
|
1230
|
+
this._subViewports.push(offsetViewport);
|
|
1231
|
+
}
|
|
1232
|
+
}
|
|
1233
|
+
return this._subViewports;
|
|
1234
|
+
}
|
|
1235
|
+
projectPosition(xyz) {
|
|
1236
|
+
if (this._pseudoMeters) {
|
|
1237
|
+
return super.projectPosition(xyz);
|
|
1238
|
+
}
|
|
1239
|
+
const [X, Y] = this.projectFlat(xyz);
|
|
1240
|
+
const Z = (xyz[2] || 0) * (0, import_web_mercator2.unitsPerMeter)(xyz[1]);
|
|
1241
|
+
return [X, Y, Z];
|
|
1242
|
+
}
|
|
1243
|
+
unprojectPosition(xyz) {
|
|
1244
|
+
if (this._pseudoMeters) {
|
|
1245
|
+
return super.unprojectPosition(xyz);
|
|
1246
|
+
}
|
|
1247
|
+
const [X, Y] = this.unprojectFlat(xyz);
|
|
1248
|
+
const Z = (xyz[2] || 0) / (0, import_web_mercator2.unitsPerMeter)(Y);
|
|
1249
|
+
return [X, Y, Z];
|
|
1250
|
+
}
|
|
1251
|
+
/**
|
|
1252
|
+
* Add a meter delta to a base lnglat coordinate, returning a new lnglat array
|
|
1253
|
+
*
|
|
1254
|
+
* Note: Uses simple linear approximation around the viewport center
|
|
1255
|
+
* Error increases with size of offset (roughly 1% per 100km)
|
|
1256
|
+
*
|
|
1257
|
+
* @param {[Number,Number]|[Number,Number,Number]) lngLatZ - base coordinate
|
|
1258
|
+
* @param {[Number,Number]|[Number,Number,Number]) xyz - array of meter deltas
|
|
1259
|
+
* @return {[Number,Number]|[Number,Number,Number]) array of [lng,lat,z] deltas
|
|
1260
|
+
*/
|
|
1261
|
+
addMetersToLngLat(lngLatZ, xyz) {
|
|
1262
|
+
return (0, import_web_mercator2.addMetersToLngLat)(lngLatZ, xyz);
|
|
1263
|
+
}
|
|
1264
|
+
panByPosition(coords, pixel, startPixel) {
|
|
1265
|
+
const fromLocation = (0, import_web_mercator2.pixelsToWorld)(pixel, this.pixelUnprojectionMatrix);
|
|
1266
|
+
const toLocation = this.projectFlat(coords);
|
|
1267
|
+
const translate = import_core3.vec2.add([], toLocation, import_core3.vec2.negate([], fromLocation));
|
|
1268
|
+
const newCenter = import_core3.vec2.add([], this.center, translate);
|
|
1269
|
+
const [longitude, latitude] = this.unprojectFlat(newCenter);
|
|
1270
|
+
return { longitude, latitude };
|
|
1271
|
+
}
|
|
1272
|
+
/**
|
|
1273
|
+
* Returns a new longitude and latitude that keeps a 3D world coordinate at a given screen pixel
|
|
1274
|
+
* This version handles the z-component (altitude) properly for cameras positioned above ground
|
|
1275
|
+
*/
|
|
1276
|
+
panByPosition3D(coords, pixel) {
|
|
1277
|
+
const targetZ = coords[2] || 0;
|
|
1278
|
+
const deltaLngLat = import_core3.vec2.sub([], coords, this.unproject(pixel, { targetZ }));
|
|
1279
|
+
return { longitude: this.longitude + deltaLngLat[0], latitude: this.latitude + deltaLngLat[1] };
|
|
1280
|
+
}
|
|
1281
|
+
getBounds(options = {}) {
|
|
1282
|
+
const corners = (0, import_web_mercator2.getBounds)(this, options.z || 0);
|
|
1283
|
+
return [
|
|
1284
|
+
Math.min(corners[0][0], corners[1][0], corners[2][0], corners[3][0]),
|
|
1285
|
+
Math.min(corners[0][1], corners[1][1], corners[2][1], corners[3][1]),
|
|
1286
|
+
Math.max(corners[0][0], corners[1][0], corners[2][0], corners[3][0]),
|
|
1287
|
+
Math.max(corners[0][1], corners[1][1], corners[2][1], corners[3][1])
|
|
1288
|
+
];
|
|
1289
|
+
}
|
|
1290
|
+
/**
|
|
1291
|
+
* Returns a new viewport that fit around the given rectangle.
|
|
1292
|
+
* Only supports non-perspective mode.
|
|
1293
|
+
*/
|
|
1294
|
+
fitBounds(bounds, options = {}) {
|
|
1295
|
+
const { width, height } = this;
|
|
1296
|
+
const { longitude, latitude, zoom } = (0, import_web_mercator2.fitBounds)({ width, height, bounds, ...options });
|
|
1297
|
+
return new _WebMercatorViewport({ width, height, longitude, latitude, zoom });
|
|
1298
|
+
}
|
|
1299
|
+
};
|
|
1300
|
+
_WebMercatorViewport.displayName = "WebMercatorViewport";
|
|
1301
|
+
var WebMercatorViewport = _WebMercatorViewport;
|
|
1302
|
+
|
|
1303
|
+
// ../core/src/passes/pick-layers-pass.ts
|
|
1304
|
+
var PICKING_BLENDING = {
|
|
1305
|
+
blendColorOperation: "add",
|
|
1306
|
+
blendColorSrcFactor: "one",
|
|
1307
|
+
blendColorDstFactor: "zero",
|
|
1308
|
+
blendAlphaOperation: "add",
|
|
1309
|
+
blendAlphaSrcFactor: "constant",
|
|
1310
|
+
blendAlphaDstFactor: "zero"
|
|
1311
|
+
};
|
|
1312
|
+
var PickLayersPass = class extends LayersPass {
|
|
1313
|
+
constructor() {
|
|
1314
|
+
super(...arguments);
|
|
1315
|
+
this._colorEncoderState = null;
|
|
1316
|
+
}
|
|
1317
|
+
render(props) {
|
|
1318
|
+
if ("pickingFBO" in props) {
|
|
1319
|
+
return this._drawPickingBuffer(props);
|
|
1320
|
+
}
|
|
1321
|
+
const stats = super._render(props);
|
|
1322
|
+
return { decodePickingColor: null, stats };
|
|
1323
|
+
}
|
|
1324
|
+
// Private
|
|
1325
|
+
// Draws list of layers and viewports into the picking buffer
|
|
1326
|
+
// Note: does not sample the buffer, that has to be done by the caller
|
|
1327
|
+
_drawPickingBuffer({
|
|
1328
|
+
layers,
|
|
1329
|
+
layerFilter,
|
|
1330
|
+
views,
|
|
1331
|
+
viewports,
|
|
1332
|
+
onViewportActive,
|
|
1333
|
+
pickingFBO,
|
|
1334
|
+
deviceRect: { x, y, width, height },
|
|
1335
|
+
cullRect,
|
|
1336
|
+
effects,
|
|
1337
|
+
pass = "picking",
|
|
1338
|
+
pickZ,
|
|
1339
|
+
canvasContext,
|
|
1340
|
+
shaderModuleProps,
|
|
1341
|
+
clearColor
|
|
1342
|
+
}) {
|
|
1343
|
+
this.pickZ = pickZ;
|
|
1344
|
+
const colorEncoderState = this._resetColorEncoder(pickZ);
|
|
1345
|
+
const scissorRect = [x, y, width, height];
|
|
1346
|
+
const renderStatus = super._render({
|
|
1347
|
+
target: pickingFBO,
|
|
1348
|
+
layers,
|
|
1349
|
+
layerFilter,
|
|
1350
|
+
views,
|
|
1351
|
+
viewports,
|
|
1352
|
+
onViewportActive,
|
|
1353
|
+
cullRect,
|
|
1354
|
+
effects: effects == null ? void 0 : effects.filter((e) => e.useInPicking),
|
|
1355
|
+
pass,
|
|
1356
|
+
canvasContext,
|
|
1357
|
+
isPicking: true,
|
|
1358
|
+
shaderModuleProps,
|
|
1359
|
+
clearColor: clearColor ?? [0, 0, 0, 0],
|
|
1360
|
+
colorMask: 15,
|
|
1361
|
+
scissorRect
|
|
1362
|
+
});
|
|
1363
|
+
this._colorEncoderState = null;
|
|
1364
|
+
const decodePickingColor = colorEncoderState && decodeColor.bind(null, colorEncoderState);
|
|
1365
|
+
return { decodePickingColor, stats: renderStatus };
|
|
1366
|
+
}
|
|
1367
|
+
shouldDrawLayer(layer) {
|
|
1368
|
+
const { pickable, operation } = layer.props;
|
|
1369
|
+
return pickable && operation.includes("draw") || operation.includes("terrain") || operation.includes("mask");
|
|
1370
|
+
}
|
|
1371
|
+
getShaderModuleProps(layer, effects, otherShaderModuleProps) {
|
|
1372
|
+
var _a2;
|
|
1373
|
+
return {
|
|
1374
|
+
picking: {
|
|
1375
|
+
isActive: 1,
|
|
1376
|
+
isAttribute: this.pickZ,
|
|
1377
|
+
disabledPickingIndices: (_a2 = layer.internalState) == null ? void 0 : _a2.disabledPickingIndices
|
|
1378
|
+
},
|
|
1379
|
+
lighting: { enabled: false }
|
|
1380
|
+
};
|
|
1381
|
+
}
|
|
1382
|
+
getLayerParameters(layer, layerIndex, viewport) {
|
|
1383
|
+
var _a2;
|
|
1384
|
+
const pickParameters = {
|
|
1385
|
+
...layer.props.parameters
|
|
1386
|
+
};
|
|
1387
|
+
const { pickable, operation } = layer.props;
|
|
1388
|
+
if (!this._colorEncoderState) {
|
|
1389
|
+
pickParameters.blend = false;
|
|
1390
|
+
} else if (pickable && operation.includes("draw")) {
|
|
1391
|
+
Object.assign(pickParameters, PICKING_BLENDING);
|
|
1392
|
+
pickParameters.blend = true;
|
|
1393
|
+
if (this.device.type === "webgpu") {
|
|
1394
|
+
pickParameters.blendConstant = encodeColor(this._colorEncoderState, layer, viewport);
|
|
1395
|
+
} else {
|
|
1396
|
+
pickParameters.blendColor = encodeColor(this._colorEncoderState, layer, viewport);
|
|
1397
|
+
}
|
|
1398
|
+
if (operation.includes("terrain") && ((_a2 = layer.state) == null ? void 0 : _a2._hasPickingCover)) {
|
|
1399
|
+
pickParameters.blendAlphaSrcFactor = "one";
|
|
1400
|
+
}
|
|
1401
|
+
} else if (operation.includes("terrain")) {
|
|
1402
|
+
pickParameters.blend = false;
|
|
1403
|
+
}
|
|
1404
|
+
return pickParameters;
|
|
1405
|
+
}
|
|
1406
|
+
_resetColorEncoder(pickZ) {
|
|
1407
|
+
this._colorEncoderState = pickZ ? null : {
|
|
1408
|
+
byLayer: /* @__PURE__ */ new Map(),
|
|
1409
|
+
byAlpha: []
|
|
1410
|
+
};
|
|
1411
|
+
return this._colorEncoderState;
|
|
1412
|
+
}
|
|
1413
|
+
};
|
|
1414
|
+
function encodeColor(encoded, layer, viewport) {
|
|
1415
|
+
const { byLayer, byAlpha } = encoded;
|
|
1416
|
+
let a;
|
|
1417
|
+
let entry = byLayer.get(layer);
|
|
1418
|
+
if (entry) {
|
|
1419
|
+
entry.viewports.push(viewport);
|
|
1420
|
+
a = entry.a;
|
|
1421
|
+
} else {
|
|
1422
|
+
a = byLayer.size + 1;
|
|
1423
|
+
if (a <= 255) {
|
|
1424
|
+
entry = { a, layer, viewports: [viewport] };
|
|
1425
|
+
byLayer.set(layer, entry);
|
|
1426
|
+
byAlpha[a] = entry;
|
|
1427
|
+
} else {
|
|
1428
|
+
log_default.warn("Too many pickable layers, only picking the first 255")();
|
|
1429
|
+
a = 0;
|
|
1430
|
+
}
|
|
1431
|
+
}
|
|
1432
|
+
return [0, 0, 0, a / 255];
|
|
1433
|
+
}
|
|
1434
|
+
function decodeColor(encoded, pickedColor) {
|
|
1435
|
+
const entry = encoded.byAlpha[pickedColor[3]];
|
|
1436
|
+
return entry && {
|
|
1437
|
+
pickedLayer: entry.layer,
|
|
1438
|
+
pickedViewports: entry.viewports,
|
|
1439
|
+
pickedObjectIndex: entry.layer.decodePickingColor(pickedColor)
|
|
1440
|
+
};
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
// ../core/src/lib/layer-manager.ts
|
|
1444
|
+
var import_engine = require("@luma.gl/engine");
|
|
1445
|
+
|
|
1446
|
+
// ../core/src/lifecycle/constants.ts
|
|
1447
|
+
var LIFECYCLE = {
|
|
1448
|
+
NO_STATE: "Awaiting state",
|
|
1449
|
+
MATCHED: "Matched. State transferred from previous layer",
|
|
1450
|
+
INITIALIZED: "Initialized",
|
|
1451
|
+
AWAITING_GC: "Discarded. Awaiting garbage collection",
|
|
1452
|
+
AWAITING_FINALIZATION: "No longer matched. Awaiting garbage collection",
|
|
1453
|
+
FINALIZED: "Finalized! Awaiting garbage collection"
|
|
1454
|
+
};
|
|
1455
|
+
|
|
1456
|
+
// ../core/src/utils/flatten.ts
|
|
1457
|
+
function flatten(array, filter = () => true) {
|
|
1458
|
+
if (!Array.isArray(array)) {
|
|
1459
|
+
return filter(array) ? [array] : [];
|
|
1460
|
+
}
|
|
1461
|
+
return flattenArray(array, filter, []);
|
|
1462
|
+
}
|
|
1463
|
+
function flattenArray(array, filter, result) {
|
|
1464
|
+
let index = -1;
|
|
1465
|
+
while (++index < array.length) {
|
|
1466
|
+
const value = array[index];
|
|
1467
|
+
if (Array.isArray(value)) {
|
|
1468
|
+
flattenArray(value, filter, result);
|
|
1469
|
+
} else if (filter(value)) {
|
|
1470
|
+
result.push(value);
|
|
1471
|
+
}
|
|
1472
|
+
}
|
|
1473
|
+
return result;
|
|
1474
|
+
}
|
|
1475
|
+
|
|
1476
|
+
// ../core/src/lib/layer-manager.ts
|
|
1477
|
+
var import_stats = require("@probe.gl/stats");
|
|
1478
|
+
|
|
1479
|
+
// ../core/src/lib/resource/resource.ts
|
|
1480
|
+
var import_core4 = require("@loaders.gl/core");
|
|
1481
|
+
var Resource = class {
|
|
1482
|
+
constructor(id, data, context) {
|
|
1483
|
+
this._loadCount = 0;
|
|
1484
|
+
this._subscribers = /* @__PURE__ */ new Set();
|
|
1485
|
+
this.id = id;
|
|
1486
|
+
this.context = context;
|
|
1487
|
+
this.setData(data);
|
|
1488
|
+
}
|
|
1489
|
+
// consumer: {onChange: Function}
|
|
1490
|
+
subscribe(consumer) {
|
|
1491
|
+
this._subscribers.add(consumer);
|
|
1492
|
+
}
|
|
1493
|
+
unsubscribe(consumer) {
|
|
1494
|
+
this._subscribers.delete(consumer);
|
|
1495
|
+
}
|
|
1496
|
+
inUse() {
|
|
1497
|
+
return this._subscribers.size > 0;
|
|
1498
|
+
}
|
|
1499
|
+
delete() {
|
|
1500
|
+
}
|
|
1501
|
+
getData() {
|
|
1502
|
+
return this.isLoaded ? this._error ? Promise.reject(this._error) : this._content : this._loader.then(() => this.getData());
|
|
1503
|
+
}
|
|
1504
|
+
setData(data, forceUpdate) {
|
|
1505
|
+
if (data === this._data && !forceUpdate) {
|
|
1506
|
+
return;
|
|
1507
|
+
}
|
|
1508
|
+
this._data = data;
|
|
1509
|
+
const loadCount = ++this._loadCount;
|
|
1510
|
+
let loader = data;
|
|
1511
|
+
if (typeof data === "string") {
|
|
1512
|
+
loader = (0, import_core4.load)(data);
|
|
1513
|
+
}
|
|
1514
|
+
if (loader instanceof Promise) {
|
|
1515
|
+
this.isLoaded = false;
|
|
1516
|
+
this._loader = loader.then((result) => {
|
|
1517
|
+
if (this._loadCount === loadCount) {
|
|
1518
|
+
this.isLoaded = true;
|
|
1519
|
+
this._error = void 0;
|
|
1520
|
+
this._content = result;
|
|
1521
|
+
}
|
|
1522
|
+
}).catch((error) => {
|
|
1523
|
+
if (this._loadCount === loadCount) {
|
|
1524
|
+
this.isLoaded = true;
|
|
1525
|
+
this._error = error || true;
|
|
1526
|
+
}
|
|
1527
|
+
});
|
|
1528
|
+
} else {
|
|
1529
|
+
this.isLoaded = true;
|
|
1530
|
+
this._error = void 0;
|
|
1531
|
+
this._content = data;
|
|
1532
|
+
}
|
|
1533
|
+
for (const subscriber of this._subscribers) {
|
|
1534
|
+
subscriber.onChange(this.getData());
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
};
|
|
1538
|
+
|
|
1539
|
+
// ../core/src/lib/resource/resource-manager.ts
|
|
1540
|
+
var ResourceManager = class {
|
|
1541
|
+
constructor(props) {
|
|
1542
|
+
var _a2;
|
|
1543
|
+
this.protocol = props.protocol || "resource://";
|
|
1544
|
+
this._context = {
|
|
1545
|
+
device: props.device,
|
|
1546
|
+
// @ts-expect-error
|
|
1547
|
+
gl: (_a2 = props.device) == null ? void 0 : _a2.gl,
|
|
1548
|
+
resourceManager: this
|
|
1549
|
+
};
|
|
1550
|
+
this._resources = {};
|
|
1551
|
+
this._consumers = {};
|
|
1552
|
+
this._pruneRequest = null;
|
|
1553
|
+
}
|
|
1554
|
+
contains(resourceId) {
|
|
1555
|
+
if (resourceId.startsWith(this.protocol)) {
|
|
1556
|
+
return true;
|
|
1557
|
+
}
|
|
1558
|
+
return resourceId in this._resources;
|
|
1559
|
+
}
|
|
1560
|
+
add({
|
|
1561
|
+
resourceId,
|
|
1562
|
+
data,
|
|
1563
|
+
forceUpdate = false,
|
|
1564
|
+
persistent = true
|
|
1565
|
+
}) {
|
|
1566
|
+
let res = this._resources[resourceId];
|
|
1567
|
+
if (res) {
|
|
1568
|
+
res.setData(data, forceUpdate);
|
|
1569
|
+
} else {
|
|
1570
|
+
res = new Resource(resourceId, data, this._context);
|
|
1571
|
+
this._resources[resourceId] = res;
|
|
1572
|
+
}
|
|
1573
|
+
res.persistent = persistent;
|
|
1574
|
+
}
|
|
1575
|
+
remove(resourceId) {
|
|
1576
|
+
const res = this._resources[resourceId];
|
|
1577
|
+
if (res) {
|
|
1578
|
+
res.delete();
|
|
1579
|
+
delete this._resources[resourceId];
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
unsubscribe({ consumerId }) {
|
|
1583
|
+
const consumer = this._consumers[consumerId];
|
|
1584
|
+
if (consumer) {
|
|
1585
|
+
for (const requestId in consumer) {
|
|
1586
|
+
const request = consumer[requestId];
|
|
1587
|
+
const resource = this._resources[request.resourceId];
|
|
1588
|
+
if (resource) {
|
|
1589
|
+
resource.unsubscribe(request);
|
|
1590
|
+
}
|
|
1591
|
+
}
|
|
1592
|
+
delete this._consumers[consumerId];
|
|
1593
|
+
this.prune();
|
|
1594
|
+
}
|
|
1595
|
+
}
|
|
1596
|
+
subscribe({
|
|
1597
|
+
resourceId,
|
|
1598
|
+
onChange,
|
|
1599
|
+
consumerId,
|
|
1600
|
+
requestId = "default"
|
|
1601
|
+
}) {
|
|
1602
|
+
const { _resources: resources, protocol } = this;
|
|
1603
|
+
if (resourceId.startsWith(protocol)) {
|
|
1604
|
+
resourceId = resourceId.replace(protocol, "");
|
|
1605
|
+
if (!resources[resourceId]) {
|
|
1606
|
+
this.add({ resourceId, data: null, persistent: false });
|
|
1607
|
+
}
|
|
1608
|
+
}
|
|
1609
|
+
const res = resources[resourceId];
|
|
1610
|
+
this._track(consumerId, requestId, res, onChange);
|
|
1611
|
+
if (res) {
|
|
1612
|
+
return res.getData();
|
|
1613
|
+
}
|
|
1614
|
+
return void 0;
|
|
1615
|
+
}
|
|
1616
|
+
prune() {
|
|
1617
|
+
if (!this._pruneRequest) {
|
|
1618
|
+
this._pruneRequest = setTimeout(() => this._prune(), 0);
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
finalize() {
|
|
1622
|
+
for (const key in this._resources) {
|
|
1623
|
+
this._resources[key].delete();
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1626
|
+
_track(consumerId, requestId, resource, onChange) {
|
|
1627
|
+
const consumers = this._consumers;
|
|
1628
|
+
const consumer = consumers[consumerId] = consumers[consumerId] || {};
|
|
1629
|
+
let request = consumer[requestId];
|
|
1630
|
+
const oldResource = request && request.resourceId && this._resources[request.resourceId];
|
|
1631
|
+
if (oldResource) {
|
|
1632
|
+
oldResource.unsubscribe(request);
|
|
1633
|
+
this.prune();
|
|
1634
|
+
}
|
|
1635
|
+
if (resource) {
|
|
1636
|
+
if (request) {
|
|
1637
|
+
request.onChange = onChange;
|
|
1638
|
+
request.resourceId = resource.id;
|
|
1639
|
+
} else {
|
|
1640
|
+
request = {
|
|
1641
|
+
onChange,
|
|
1642
|
+
resourceId: resource.id
|
|
1643
|
+
};
|
|
1644
|
+
}
|
|
1645
|
+
consumer[requestId] = request;
|
|
1646
|
+
resource.subscribe(request);
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
_prune() {
|
|
1650
|
+
this._pruneRequest = null;
|
|
1651
|
+
for (const key of Object.keys(this._resources)) {
|
|
1652
|
+
const res = this._resources[key];
|
|
1653
|
+
if (!res.persistent && !res.inUse()) {
|
|
1654
|
+
res.delete();
|
|
1655
|
+
delete this._resources[key];
|
|
1656
|
+
}
|
|
1657
|
+
}
|
|
1658
|
+
}
|
|
1659
|
+
};
|
|
1660
|
+
|
|
1661
|
+
// ../core/src/lib/layer-manager.ts
|
|
1662
|
+
var TRACE_SET_LAYERS = "layerManager.setLayers";
|
|
1663
|
+
var TRACE_ACTIVATE_VIEWPORT = "layerManager.activateViewport";
|
|
1664
|
+
var LayerManager = class {
|
|
1665
|
+
/**
|
|
1666
|
+
* @param device
|
|
1667
|
+
* @param param1
|
|
1668
|
+
*/
|
|
1669
|
+
// eslint-disable-next-line
|
|
1670
|
+
constructor(device2, props) {
|
|
1671
|
+
this._lastRenderedLayers = [];
|
|
1672
|
+
this._needsRedraw = false;
|
|
1673
|
+
this._needsUpdate = false;
|
|
1674
|
+
this._nextLayers = null;
|
|
1675
|
+
this._debug = false;
|
|
1676
|
+
// This flag is separate from _needsUpdate because it can be set during an update and should trigger another full update
|
|
1677
|
+
this._defaultShaderModulesChanged = false;
|
|
1678
|
+
//
|
|
1679
|
+
// INTERNAL METHODS
|
|
1680
|
+
//
|
|
1681
|
+
/** Make a viewport "current" in layer context, updating viewportChanged flags */
|
|
1682
|
+
this.activateViewport = (viewport) => {
|
|
1683
|
+
debug(TRACE_ACTIVATE_VIEWPORT, this, viewport);
|
|
1684
|
+
if (viewport) {
|
|
1685
|
+
this.context.viewport = viewport;
|
|
1686
|
+
}
|
|
1687
|
+
};
|
|
1688
|
+
var _a2;
|
|
1689
|
+
const { deck, stats, viewport, timeline } = props || {};
|
|
1690
|
+
this.layers = [];
|
|
1691
|
+
this.resourceManager = new ResourceManager({ device: device2, protocol: "deck://" });
|
|
1692
|
+
this.context = {
|
|
1693
|
+
mousePosition: null,
|
|
1694
|
+
userData: {},
|
|
1695
|
+
layerManager: this,
|
|
1696
|
+
device: device2,
|
|
1697
|
+
// @ts-expect-error
|
|
1698
|
+
gl: device2 == null ? void 0 : device2.gl,
|
|
1699
|
+
deck,
|
|
1700
|
+
shaderAssembler: getShaderAssembler(((_a2 = device2 == null ? void 0 : device2.info) == null ? void 0 : _a2.shadingLanguage) || "glsl"),
|
|
1701
|
+
defaultShaderModules: [layerUniforms],
|
|
1702
|
+
renderPass: void 0,
|
|
1703
|
+
stats: stats || new import_stats.Stats({ id: "deck.gl" }),
|
|
1704
|
+
// Make sure context.viewport is not empty on the first layer initialization
|
|
1705
|
+
viewport: viewport || new Viewport({ id: "DEFAULT-INITIAL-VIEWPORT" }),
|
|
1706
|
+
// Current viewport, exposed to layers for project* function
|
|
1707
|
+
timeline: timeline || new import_engine.Timeline(),
|
|
1708
|
+
resourceManager: this.resourceManager,
|
|
1709
|
+
onError: void 0
|
|
1710
|
+
};
|
|
1711
|
+
Object.seal(this);
|
|
1712
|
+
}
|
|
1713
|
+
/** Method to call when the layer manager is not needed anymore. */
|
|
1714
|
+
finalize() {
|
|
1715
|
+
this.resourceManager.finalize();
|
|
1716
|
+
for (const layer of this.layers) {
|
|
1717
|
+
this._finalizeLayer(layer);
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
/** Check if a redraw is needed */
|
|
1721
|
+
needsRedraw(opts = { clearRedrawFlags: false }) {
|
|
1722
|
+
let redraw = this._needsRedraw;
|
|
1723
|
+
if (opts.clearRedrawFlags) {
|
|
1724
|
+
this._needsRedraw = false;
|
|
1725
|
+
}
|
|
1726
|
+
for (const layer of this.layers) {
|
|
1727
|
+
const layerNeedsRedraw = layer.getNeedsRedraw(opts);
|
|
1728
|
+
redraw = redraw || layerNeedsRedraw;
|
|
1729
|
+
}
|
|
1730
|
+
return redraw;
|
|
1731
|
+
}
|
|
1732
|
+
/** Check if a deep update of all layers is needed */
|
|
1733
|
+
needsUpdate() {
|
|
1734
|
+
if (this._nextLayers && this._nextLayers !== this._lastRenderedLayers) {
|
|
1735
|
+
return "layers changed";
|
|
1736
|
+
}
|
|
1737
|
+
if (this._defaultShaderModulesChanged) {
|
|
1738
|
+
return "shader modules changed";
|
|
1739
|
+
}
|
|
1740
|
+
return this._needsUpdate;
|
|
1741
|
+
}
|
|
1742
|
+
/** Layers will be redrawn (in next animation frame) */
|
|
1743
|
+
setNeedsRedraw(reason) {
|
|
1744
|
+
this._needsRedraw = this._needsRedraw || reason;
|
|
1745
|
+
}
|
|
1746
|
+
/** Layers will be updated deeply (in next animation frame)
|
|
1747
|
+
Potentially regenerating attributes and sub layers */
|
|
1748
|
+
setNeedsUpdate(reason) {
|
|
1749
|
+
this._needsUpdate = this._needsUpdate || reason;
|
|
1750
|
+
}
|
|
1751
|
+
/** Gets a list of currently rendered layers. Optionally filter by id. */
|
|
1752
|
+
getLayers({ layerIds } = {}) {
|
|
1753
|
+
return layerIds ? this.layers.filter((layer) => layerIds.find((layerId) => layer.id.indexOf(layerId) === 0)) : this.layers;
|
|
1754
|
+
}
|
|
1755
|
+
/** Set props needed for layer rendering and picking. */
|
|
1756
|
+
setProps(props) {
|
|
1757
|
+
if ("debug" in props) {
|
|
1758
|
+
this._debug = props.debug;
|
|
1759
|
+
}
|
|
1760
|
+
if ("userData" in props) {
|
|
1761
|
+
this.context.userData = props.userData;
|
|
1762
|
+
}
|
|
1763
|
+
if ("layers" in props) {
|
|
1764
|
+
this._nextLayers = props.layers;
|
|
1765
|
+
}
|
|
1766
|
+
if ("onError" in props) {
|
|
1767
|
+
this.context.onError = props.onError;
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
/** Supply a new layer list, initiating sublayer generation and layer matching */
|
|
1771
|
+
setLayers(newLayers, reason) {
|
|
1772
|
+
debug(TRACE_SET_LAYERS, this, reason, newLayers);
|
|
1773
|
+
this._lastRenderedLayers = newLayers;
|
|
1774
|
+
const flatLayers = flatten(newLayers, Boolean);
|
|
1775
|
+
for (const layer of flatLayers) {
|
|
1776
|
+
layer.context = this.context;
|
|
1777
|
+
}
|
|
1778
|
+
this._updateLayers(this.layers, flatLayers);
|
|
1779
|
+
}
|
|
1780
|
+
/** Update layers from last cycle if `setNeedsUpdate()` has been called */
|
|
1781
|
+
updateLayers() {
|
|
1782
|
+
const reason = this.needsUpdate();
|
|
1783
|
+
if (reason) {
|
|
1784
|
+
this.setNeedsRedraw(`updating layers: ${reason}`);
|
|
1785
|
+
this.setLayers(this._nextLayers || this._lastRenderedLayers, reason);
|
|
1786
|
+
}
|
|
1787
|
+
this._nextLayers = null;
|
|
1788
|
+
}
|
|
1789
|
+
/** Register a default shader module */
|
|
1790
|
+
addDefaultShaderModule(module2) {
|
|
1791
|
+
const { defaultShaderModules } = this.context;
|
|
1792
|
+
if (!defaultShaderModules.find((m) => m.name === module2.name)) {
|
|
1793
|
+
defaultShaderModules.push(module2);
|
|
1794
|
+
this._defaultShaderModulesChanged = true;
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
/** Deregister a default shader module */
|
|
1798
|
+
removeDefaultShaderModule(module2) {
|
|
1799
|
+
const { defaultShaderModules } = this.context;
|
|
1800
|
+
const i = defaultShaderModules.findIndex((m) => m.name === module2.name);
|
|
1801
|
+
if (i >= 0) {
|
|
1802
|
+
defaultShaderModules.splice(i, 1);
|
|
1803
|
+
this._defaultShaderModulesChanged = true;
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
_handleError(stage, error, layer) {
|
|
1807
|
+
layer.raiseError(error, `${stage} of ${layer}`);
|
|
1808
|
+
}
|
|
1809
|
+
// TODO - mark layers with exceptions as bad and remove from rendering cycle?
|
|
1810
|
+
/** Match all layers, checking for caught errors
|
|
1811
|
+
to avoid having an exception in one layer disrupt other layers */
|
|
1812
|
+
_updateLayers(oldLayers, newLayers) {
|
|
1813
|
+
const oldLayerMap = {};
|
|
1814
|
+
for (const oldLayer of oldLayers) {
|
|
1815
|
+
if (oldLayerMap[oldLayer.id]) {
|
|
1816
|
+
log_default.warn(`Multiple old layers with same id ${oldLayer.id}`)();
|
|
1817
|
+
} else {
|
|
1818
|
+
oldLayerMap[oldLayer.id] = oldLayer;
|
|
1819
|
+
}
|
|
1820
|
+
}
|
|
1821
|
+
if (this._defaultShaderModulesChanged) {
|
|
1822
|
+
for (const layer of oldLayers) {
|
|
1823
|
+
layer.setNeedsUpdate();
|
|
1824
|
+
layer.setChangeFlags({ extensionsChanged: true });
|
|
1825
|
+
}
|
|
1826
|
+
this._defaultShaderModulesChanged = false;
|
|
1827
|
+
}
|
|
1828
|
+
const generatedLayers = [];
|
|
1829
|
+
this._updateSublayersRecursively(newLayers, oldLayerMap, generatedLayers);
|
|
1830
|
+
this._finalizeOldLayers(oldLayerMap);
|
|
1831
|
+
let needsUpdate = false;
|
|
1832
|
+
for (const layer of generatedLayers) {
|
|
1833
|
+
if (layer.hasUniformTransition()) {
|
|
1834
|
+
needsUpdate = `Uniform transition in ${layer}`;
|
|
1835
|
+
break;
|
|
1836
|
+
}
|
|
1837
|
+
}
|
|
1838
|
+
this._needsUpdate = needsUpdate;
|
|
1839
|
+
this.layers = generatedLayers;
|
|
1840
|
+
}
|
|
1841
|
+
/* eslint-disable complexity,max-statements */
|
|
1842
|
+
// Note: adds generated layers to `generatedLayers` array parameter
|
|
1843
|
+
_updateSublayersRecursively(newLayers, oldLayerMap, generatedLayers) {
|
|
1844
|
+
for (const newLayer of newLayers) {
|
|
1845
|
+
newLayer.context = this.context;
|
|
1846
|
+
const oldLayer = oldLayerMap[newLayer.id];
|
|
1847
|
+
if (oldLayer === null) {
|
|
1848
|
+
log_default.warn(`Multiple new layers with same id ${newLayer.id}`)();
|
|
1849
|
+
}
|
|
1850
|
+
oldLayerMap[newLayer.id] = null;
|
|
1851
|
+
let sublayers = null;
|
|
1852
|
+
try {
|
|
1853
|
+
if (this._debug && oldLayer !== newLayer) {
|
|
1854
|
+
newLayer.validateProps();
|
|
1855
|
+
}
|
|
1856
|
+
if (!oldLayer) {
|
|
1857
|
+
this._initializeLayer(newLayer);
|
|
1858
|
+
} else {
|
|
1859
|
+
this._transferLayerState(oldLayer, newLayer);
|
|
1860
|
+
this._updateLayer(newLayer);
|
|
1861
|
+
}
|
|
1862
|
+
generatedLayers.push(newLayer);
|
|
1863
|
+
sublayers = newLayer.isComposite ? newLayer.getSubLayers() : null;
|
|
1864
|
+
} catch (err) {
|
|
1865
|
+
this._handleError("matching", err, newLayer);
|
|
1866
|
+
}
|
|
1867
|
+
if (sublayers) {
|
|
1868
|
+
this._updateSublayersRecursively(sublayers, oldLayerMap, generatedLayers);
|
|
1869
|
+
}
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
/* eslint-enable complexity,max-statements */
|
|
1873
|
+
// Finalize any old layers that were not matched
|
|
1874
|
+
_finalizeOldLayers(oldLayerMap) {
|
|
1875
|
+
for (const layerId in oldLayerMap) {
|
|
1876
|
+
const layer = oldLayerMap[layerId];
|
|
1877
|
+
if (layer) {
|
|
1878
|
+
this._finalizeLayer(layer);
|
|
1879
|
+
}
|
|
1880
|
+
}
|
|
1881
|
+
}
|
|
1882
|
+
// / EXCEPTION SAFE LAYER ACCESS
|
|
1883
|
+
/** Safely initializes a single layer, calling layer methods */
|
|
1884
|
+
_initializeLayer(layer) {
|
|
1885
|
+
try {
|
|
1886
|
+
layer._initialize();
|
|
1887
|
+
layer.lifecycle = LIFECYCLE.INITIALIZED;
|
|
1888
|
+
} catch (err) {
|
|
1889
|
+
this._handleError("initialization", err, layer);
|
|
1890
|
+
}
|
|
1891
|
+
}
|
|
1892
|
+
/** Transfer state from one layer to a newer version */
|
|
1893
|
+
_transferLayerState(oldLayer, newLayer) {
|
|
1894
|
+
newLayer._transferState(oldLayer);
|
|
1895
|
+
newLayer.lifecycle = LIFECYCLE.MATCHED;
|
|
1896
|
+
if (newLayer !== oldLayer) {
|
|
1897
|
+
oldLayer.lifecycle = LIFECYCLE.AWAITING_GC;
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
/** Safely updates a single layer, cleaning all flags */
|
|
1901
|
+
_updateLayer(layer) {
|
|
1902
|
+
try {
|
|
1903
|
+
layer._update();
|
|
1904
|
+
} catch (err) {
|
|
1905
|
+
this._handleError("update", err, layer);
|
|
1906
|
+
}
|
|
1907
|
+
}
|
|
1908
|
+
/** Safely finalizes a single layer, removing all resources */
|
|
1909
|
+
_finalizeLayer(layer) {
|
|
1910
|
+
this._needsRedraw = this._needsRedraw || `finalized ${layer}`;
|
|
1911
|
+
layer.lifecycle = LIFECYCLE.AWAITING_FINALIZATION;
|
|
1912
|
+
try {
|
|
1913
|
+
layer._finalize();
|
|
1914
|
+
layer.lifecycle = LIFECYCLE.FINALIZED;
|
|
1915
|
+
} catch (err) {
|
|
1916
|
+
this._handleError("finalization", err, layer);
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
};
|
|
1920
|
+
|
|
1921
|
+
// ../core/src/utils/deep-equal.ts
|
|
1922
|
+
function deepEqual(a, b, depth) {
|
|
1923
|
+
if (a === b) {
|
|
1924
|
+
return true;
|
|
1925
|
+
}
|
|
1926
|
+
if (!depth || !a || !b) {
|
|
1927
|
+
return false;
|
|
1928
|
+
}
|
|
1929
|
+
if (Array.isArray(a)) {
|
|
1930
|
+
if (!Array.isArray(b) || a.length !== b.length) {
|
|
1931
|
+
return false;
|
|
1932
|
+
}
|
|
1933
|
+
for (let i = 0; i < a.length; i++) {
|
|
1934
|
+
if (!deepEqual(a[i], b[i], depth - 1)) {
|
|
1935
|
+
return false;
|
|
1936
|
+
}
|
|
1937
|
+
}
|
|
1938
|
+
return true;
|
|
1939
|
+
}
|
|
1940
|
+
if (Array.isArray(b)) {
|
|
1941
|
+
return false;
|
|
1942
|
+
}
|
|
1943
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
1944
|
+
const aKeys = Object.keys(a);
|
|
1945
|
+
const bKeys = Object.keys(b);
|
|
1946
|
+
if (aKeys.length !== bKeys.length) {
|
|
1947
|
+
return false;
|
|
1948
|
+
}
|
|
1949
|
+
for (const key of aKeys) {
|
|
1950
|
+
if (!b.hasOwnProperty(key)) {
|
|
1951
|
+
return false;
|
|
1952
|
+
}
|
|
1953
|
+
if (!deepEqual(a[key], b[key], depth - 1)) {
|
|
1954
|
+
return false;
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1957
|
+
return true;
|
|
1958
|
+
}
|
|
1959
|
+
return false;
|
|
1960
|
+
}
|
|
1961
|
+
|
|
1962
|
+
// ../core/src/utils/positions.ts
|
|
1963
|
+
var NUMBER_REGEX = /^(?:\d+\.?\d*|\.\d+)$/;
|
|
1964
|
+
function parsePosition(value) {
|
|
1965
|
+
switch (typeof value) {
|
|
1966
|
+
case "number":
|
|
1967
|
+
if (!Number.isFinite(value)) {
|
|
1968
|
+
throw new Error(`Could not parse position string ${value}`);
|
|
1969
|
+
}
|
|
1970
|
+
return { type: "literal", value };
|
|
1971
|
+
case "string":
|
|
1972
|
+
try {
|
|
1973
|
+
const tokens = tokenize(value);
|
|
1974
|
+
const parser = new LayoutExpressionParser(tokens);
|
|
1975
|
+
return parser.parseExpression();
|
|
1976
|
+
} catch (error) {
|
|
1977
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1978
|
+
throw new Error(`Could not parse position string ${value}: ${reason}`);
|
|
1979
|
+
}
|
|
1980
|
+
default:
|
|
1981
|
+
throw new Error(`Could not parse position string ${value}`);
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
function evaluateLayoutExpression(expression, extent) {
|
|
1985
|
+
switch (expression.type) {
|
|
1986
|
+
case "literal":
|
|
1987
|
+
return expression.value;
|
|
1988
|
+
case "percentage":
|
|
1989
|
+
return Math.round(expression.value * extent);
|
|
1990
|
+
case "binary":
|
|
1991
|
+
const left = evaluateLayoutExpression(expression.left, extent);
|
|
1992
|
+
const right = evaluateLayoutExpression(expression.right, extent);
|
|
1993
|
+
return expression.operator === "+" ? left + right : left - right;
|
|
1994
|
+
default:
|
|
1995
|
+
throw new Error("Unknown layout expression type");
|
|
1996
|
+
}
|
|
1997
|
+
}
|
|
1998
|
+
function getPosition(expression, extent) {
|
|
1999
|
+
return evaluateLayoutExpression(expression, extent);
|
|
2000
|
+
}
|
|
2001
|
+
function tokenize(input) {
|
|
2002
|
+
const tokens = [];
|
|
2003
|
+
let index = 0;
|
|
2004
|
+
while (index < input.length) {
|
|
2005
|
+
const char = input[index];
|
|
2006
|
+
if (/\s/.test(char)) {
|
|
2007
|
+
index++;
|
|
2008
|
+
continue;
|
|
2009
|
+
}
|
|
2010
|
+
if (char === "+" || char === "-" || char === "(" || char === ")" || char === "%") {
|
|
2011
|
+
tokens.push({ type: "symbol", value: char });
|
|
2012
|
+
index++;
|
|
2013
|
+
continue;
|
|
2014
|
+
}
|
|
2015
|
+
if (isDigit(char) || char === ".") {
|
|
2016
|
+
const start = index;
|
|
2017
|
+
let hasDecimal = char === ".";
|
|
2018
|
+
index++;
|
|
2019
|
+
while (index < input.length) {
|
|
2020
|
+
const next = input[index];
|
|
2021
|
+
if (isDigit(next)) {
|
|
2022
|
+
index++;
|
|
2023
|
+
continue;
|
|
2024
|
+
}
|
|
2025
|
+
if (next === "." && !hasDecimal) {
|
|
2026
|
+
hasDecimal = true;
|
|
2027
|
+
index++;
|
|
2028
|
+
continue;
|
|
2029
|
+
}
|
|
2030
|
+
break;
|
|
2031
|
+
}
|
|
2032
|
+
const numberString = input.slice(start, index);
|
|
2033
|
+
if (!NUMBER_REGEX.test(numberString)) {
|
|
2034
|
+
throw new Error("Invalid number token");
|
|
2035
|
+
}
|
|
2036
|
+
tokens.push({ type: "number", value: parseFloat(numberString) });
|
|
2037
|
+
continue;
|
|
2038
|
+
}
|
|
2039
|
+
if (isAlpha(char)) {
|
|
2040
|
+
const start = index;
|
|
2041
|
+
while (index < input.length && isAlpha(input[index])) {
|
|
2042
|
+
index++;
|
|
2043
|
+
}
|
|
2044
|
+
const word = input.slice(start, index).toLowerCase();
|
|
2045
|
+
tokens.push({ type: "word", value: word });
|
|
2046
|
+
continue;
|
|
2047
|
+
}
|
|
2048
|
+
throw new Error("Invalid token in position string");
|
|
2049
|
+
}
|
|
2050
|
+
return tokens;
|
|
2051
|
+
}
|
|
2052
|
+
var LayoutExpressionParser = class {
|
|
2053
|
+
constructor(tokens) {
|
|
2054
|
+
this.index = 0;
|
|
2055
|
+
this.tokens = tokens;
|
|
2056
|
+
}
|
|
2057
|
+
parseExpression() {
|
|
2058
|
+
const expression = this.parseBinaryExpression();
|
|
2059
|
+
if (this.index < this.tokens.length) {
|
|
2060
|
+
throw new Error("Unexpected token at end of expression");
|
|
2061
|
+
}
|
|
2062
|
+
return expression;
|
|
2063
|
+
}
|
|
2064
|
+
parseBinaryExpression() {
|
|
2065
|
+
let expression = this.parseFactor();
|
|
2066
|
+
let token = this.peek();
|
|
2067
|
+
while (isAddSubSymbol(token)) {
|
|
2068
|
+
this.index++;
|
|
2069
|
+
const right = this.parseFactor();
|
|
2070
|
+
expression = { type: "binary", operator: token.value, left: expression, right };
|
|
2071
|
+
token = this.peek();
|
|
2072
|
+
}
|
|
2073
|
+
return expression;
|
|
2074
|
+
}
|
|
2075
|
+
parseFactor() {
|
|
2076
|
+
const token = this.peek();
|
|
2077
|
+
if (!token) {
|
|
2078
|
+
throw new Error("Unexpected end of expression");
|
|
2079
|
+
}
|
|
2080
|
+
if (token.type === "symbol" && token.value === "+") {
|
|
2081
|
+
this.index++;
|
|
2082
|
+
return this.parseFactor();
|
|
2083
|
+
}
|
|
2084
|
+
if (token.type === "symbol" && token.value === "-") {
|
|
2085
|
+
this.index++;
|
|
2086
|
+
const factor = this.parseFactor();
|
|
2087
|
+
return { type: "binary", operator: "-", left: { type: "literal", value: 0 }, right: factor };
|
|
2088
|
+
}
|
|
2089
|
+
if (token.type === "symbol" && token.value === "(") {
|
|
2090
|
+
this.index++;
|
|
2091
|
+
const expression = this.parseBinaryExpression();
|
|
2092
|
+
if (!this.consumeSymbol(")")) {
|
|
2093
|
+
throw new Error("Missing closing parenthesis");
|
|
2094
|
+
}
|
|
2095
|
+
return expression;
|
|
2096
|
+
}
|
|
2097
|
+
if (token.type === "word" && token.value === "calc") {
|
|
2098
|
+
this.index++;
|
|
2099
|
+
if (!this.consumeSymbol("(")) {
|
|
2100
|
+
throw new Error("Missing opening parenthesis after calc");
|
|
2101
|
+
}
|
|
2102
|
+
const expression = this.parseBinaryExpression();
|
|
2103
|
+
if (!this.consumeSymbol(")")) {
|
|
2104
|
+
throw new Error("Missing closing parenthesis");
|
|
2105
|
+
}
|
|
2106
|
+
return expression;
|
|
2107
|
+
}
|
|
2108
|
+
if (token.type === "number") {
|
|
2109
|
+
this.index++;
|
|
2110
|
+
const numberValue = token.value;
|
|
2111
|
+
const nextToken = this.peek();
|
|
2112
|
+
if (nextToken && nextToken.type === "symbol" && nextToken.value === "%") {
|
|
2113
|
+
this.index++;
|
|
2114
|
+
return { type: "percentage", value: numberValue / 100 };
|
|
2115
|
+
}
|
|
2116
|
+
if (nextToken && nextToken.type === "word" && nextToken.value === "px") {
|
|
2117
|
+
this.index++;
|
|
2118
|
+
return { type: "literal", value: numberValue };
|
|
2119
|
+
}
|
|
2120
|
+
return { type: "literal", value: numberValue };
|
|
2121
|
+
}
|
|
2122
|
+
throw new Error("Unexpected token in expression");
|
|
2123
|
+
}
|
|
2124
|
+
consumeSymbol(value) {
|
|
2125
|
+
const token = this.peek();
|
|
2126
|
+
if (token && token.type === "symbol" && token.value === value) {
|
|
2127
|
+
this.index++;
|
|
2128
|
+
return true;
|
|
2129
|
+
}
|
|
2130
|
+
return false;
|
|
2131
|
+
}
|
|
2132
|
+
peek() {
|
|
2133
|
+
return this.tokens[this.index] || null;
|
|
2134
|
+
}
|
|
2135
|
+
};
|
|
2136
|
+
function isDigit(char) {
|
|
2137
|
+
return char >= "0" && char <= "9";
|
|
2138
|
+
}
|
|
2139
|
+
function isAlpha(char) {
|
|
2140
|
+
return char >= "a" && char <= "z" || char >= "A" && char <= "Z";
|
|
2141
|
+
}
|
|
2142
|
+
function isAddSubSymbol(token) {
|
|
2143
|
+
return Boolean(token && token.type === "symbol" && (token.value === "+" || token.value === "-"));
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
// ../core/src/utils/deep-merge.ts
|
|
2147
|
+
function deepMergeViewState(a, b) {
|
|
2148
|
+
const result = { ...a };
|
|
2149
|
+
for (const key in b) {
|
|
2150
|
+
if (key === "id") continue;
|
|
2151
|
+
if (Array.isArray(result[key]) && Array.isArray(b[key])) {
|
|
2152
|
+
result[key] = mergeNumericArray(result[key], b[key]);
|
|
2153
|
+
} else {
|
|
2154
|
+
result[key] = b[key];
|
|
2155
|
+
}
|
|
2156
|
+
}
|
|
2157
|
+
return result;
|
|
2158
|
+
}
|
|
2159
|
+
function mergeNumericArray(target, source2) {
|
|
2160
|
+
target = target.slice();
|
|
2161
|
+
for (let i = 0; i < source2.length; i++) {
|
|
2162
|
+
const v = source2[i];
|
|
2163
|
+
if (Number.isFinite(v)) {
|
|
2164
|
+
target[i] = v;
|
|
2165
|
+
}
|
|
2166
|
+
}
|
|
2167
|
+
return target;
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
// ../core/src/views/view.ts
|
|
2171
|
+
var View = class {
|
|
2172
|
+
constructor(props) {
|
|
2173
|
+
const { id, x = 0, y = 0, width = "100%", height = "100%", padding = null } = props;
|
|
2174
|
+
this.id = id || this.constructor.displayName || "view";
|
|
2175
|
+
this.props = { ...props, id: this.id };
|
|
2176
|
+
this._x = parsePosition(x);
|
|
2177
|
+
this._y = parsePosition(y);
|
|
2178
|
+
this._width = parsePosition(width);
|
|
2179
|
+
this._height = parsePosition(height);
|
|
2180
|
+
this._padding = padding && {
|
|
2181
|
+
left: parsePosition(padding.left || 0),
|
|
2182
|
+
right: parsePosition(padding.right || 0),
|
|
2183
|
+
top: parsePosition(padding.top || 0),
|
|
2184
|
+
bottom: parsePosition(padding.bottom || 0)
|
|
2185
|
+
};
|
|
2186
|
+
this.equals = this.equals.bind(this);
|
|
2187
|
+
Object.seal(this);
|
|
2188
|
+
}
|
|
2189
|
+
equals(view) {
|
|
2190
|
+
if (this === view) {
|
|
2191
|
+
return true;
|
|
2192
|
+
}
|
|
2193
|
+
return this.constructor === view.constructor && deepEqual(this.props, view.props, 2);
|
|
2194
|
+
}
|
|
2195
|
+
/** Clone this view with modified props */
|
|
2196
|
+
clone(newProps) {
|
|
2197
|
+
const ViewConstructor = this.constructor;
|
|
2198
|
+
return new ViewConstructor({ ...this.props, ...newProps });
|
|
2199
|
+
}
|
|
2200
|
+
/** Make viewport from canvas dimensions and view state */
|
|
2201
|
+
makeViewport({ width, height, viewState }) {
|
|
2202
|
+
viewState = this.filterViewState(viewState);
|
|
2203
|
+
const viewportDimensions = this.getDimensions({ width, height });
|
|
2204
|
+
if (!viewportDimensions.height || !viewportDimensions.width) {
|
|
2205
|
+
return null;
|
|
2206
|
+
}
|
|
2207
|
+
const ViewportType = this.getViewportType(viewState);
|
|
2208
|
+
return new ViewportType({ ...viewState, ...this.props, ...viewportDimensions });
|
|
2209
|
+
}
|
|
2210
|
+
getViewStateId() {
|
|
2211
|
+
const { viewState } = this.props;
|
|
2212
|
+
if (typeof viewState === "string") {
|
|
2213
|
+
return viewState;
|
|
2214
|
+
}
|
|
2215
|
+
return (viewState == null ? void 0 : viewState.id) || this.id;
|
|
2216
|
+
}
|
|
2217
|
+
// Allows view to override (or completely define) viewState
|
|
2218
|
+
filterViewState(viewState) {
|
|
2219
|
+
if (this.props.viewState && typeof this.props.viewState === "object") {
|
|
2220
|
+
if (!this.props.viewState.id) {
|
|
2221
|
+
return this.props.viewState;
|
|
2222
|
+
}
|
|
2223
|
+
return deepMergeViewState(viewState, this.props.viewState);
|
|
2224
|
+
}
|
|
2225
|
+
return viewState;
|
|
2226
|
+
}
|
|
2227
|
+
/** Resolve the dimensions of the view from overall canvas dimensions */
|
|
2228
|
+
getDimensions({ width, height }) {
|
|
2229
|
+
const dimensions = {
|
|
2230
|
+
x: getPosition(this._x, width),
|
|
2231
|
+
y: getPosition(this._y, height),
|
|
2232
|
+
width: getPosition(this._width, width),
|
|
2233
|
+
height: getPosition(this._height, height)
|
|
2234
|
+
};
|
|
2235
|
+
if (this._padding) {
|
|
2236
|
+
dimensions.padding = {
|
|
2237
|
+
left: getPosition(this._padding.left, width),
|
|
2238
|
+
top: getPosition(this._padding.top, height),
|
|
2239
|
+
right: getPosition(this._padding.right, width),
|
|
2240
|
+
bottom: getPosition(this._padding.bottom, height)
|
|
2241
|
+
};
|
|
2242
|
+
}
|
|
2243
|
+
return dimensions;
|
|
2244
|
+
}
|
|
2245
|
+
// Used by sub classes to resolve controller props
|
|
2246
|
+
get controller() {
|
|
2247
|
+
const opts = this.props.controller;
|
|
2248
|
+
if (!opts) {
|
|
2249
|
+
return null;
|
|
2250
|
+
}
|
|
2251
|
+
if (opts === true) {
|
|
2252
|
+
return { type: this.ControllerType };
|
|
2253
|
+
}
|
|
2254
|
+
if (typeof opts === "function") {
|
|
2255
|
+
return { type: opts };
|
|
2256
|
+
}
|
|
2257
|
+
return { type: this.ControllerType, ...opts };
|
|
2258
|
+
}
|
|
2259
|
+
};
|
|
2260
|
+
|
|
2261
|
+
// ../core/src/controllers/map-controller.ts
|
|
2262
|
+
var import_core9 = require("@math.gl/core");
|
|
2263
|
+
|
|
2264
|
+
// ../core/src/transitions/transition.ts
|
|
2265
|
+
var Transition = class {
|
|
2266
|
+
/**
|
|
2267
|
+
* @params timeline {Timeline}
|
|
2268
|
+
*/
|
|
2269
|
+
constructor(timeline) {
|
|
2270
|
+
this._inProgress = false;
|
|
2271
|
+
this._handle = null;
|
|
2272
|
+
this.time = 0;
|
|
2273
|
+
// @ts-expect-error
|
|
2274
|
+
this.settings = {
|
|
2275
|
+
duration: 0
|
|
2276
|
+
};
|
|
2277
|
+
this._timeline = timeline;
|
|
2278
|
+
}
|
|
2279
|
+
/* Public API */
|
|
2280
|
+
get inProgress() {
|
|
2281
|
+
return this._inProgress;
|
|
2282
|
+
}
|
|
2283
|
+
/**
|
|
2284
|
+
* (re)start this transition.
|
|
2285
|
+
* @params props {object} - optional overriding props. see constructor
|
|
2286
|
+
*/
|
|
2287
|
+
start(settings) {
|
|
2288
|
+
var _a2, _b;
|
|
2289
|
+
this.cancel();
|
|
2290
|
+
this.settings = settings;
|
|
2291
|
+
this._inProgress = true;
|
|
2292
|
+
(_b = (_a2 = this.settings).onStart) == null ? void 0 : _b.call(_a2, this);
|
|
2293
|
+
}
|
|
2294
|
+
/**
|
|
2295
|
+
* end this transition if it is in progress.
|
|
2296
|
+
*/
|
|
2297
|
+
end() {
|
|
2298
|
+
var _a2, _b;
|
|
2299
|
+
if (this._inProgress) {
|
|
2300
|
+
this._timeline.removeChannel(this._handle);
|
|
2301
|
+
this._handle = null;
|
|
2302
|
+
this._inProgress = false;
|
|
2303
|
+
(_b = (_a2 = this.settings).onEnd) == null ? void 0 : _b.call(_a2, this);
|
|
2304
|
+
}
|
|
2305
|
+
}
|
|
2306
|
+
/**
|
|
2307
|
+
* cancel this transition if it is in progress.
|
|
2308
|
+
*/
|
|
2309
|
+
cancel() {
|
|
2310
|
+
var _a2, _b;
|
|
2311
|
+
if (this._inProgress) {
|
|
2312
|
+
(_b = (_a2 = this.settings).onInterrupt) == null ? void 0 : _b.call(_a2, this);
|
|
2313
|
+
this._timeline.removeChannel(this._handle);
|
|
2314
|
+
this._handle = null;
|
|
2315
|
+
this._inProgress = false;
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
/**
|
|
2319
|
+
* update this transition. Returns `true` if updated.
|
|
2320
|
+
*/
|
|
2321
|
+
update() {
|
|
2322
|
+
var _a2, _b;
|
|
2323
|
+
if (!this._inProgress) {
|
|
2324
|
+
return false;
|
|
2325
|
+
}
|
|
2326
|
+
if (this._handle === null) {
|
|
2327
|
+
const { _timeline: timeline, settings } = this;
|
|
2328
|
+
this._handle = timeline.addChannel({
|
|
2329
|
+
delay: timeline.getTime(),
|
|
2330
|
+
duration: settings.duration
|
|
2331
|
+
});
|
|
2332
|
+
}
|
|
2333
|
+
this.time = this._timeline.getTime(this._handle);
|
|
2334
|
+
this._onUpdate();
|
|
2335
|
+
(_b = (_a2 = this.settings).onUpdate) == null ? void 0 : _b.call(_a2, this);
|
|
2336
|
+
if (this._timeline.isFinished(this._handle)) {
|
|
2337
|
+
this.end();
|
|
2338
|
+
}
|
|
2339
|
+
return true;
|
|
2340
|
+
}
|
|
2341
|
+
/* Private API */
|
|
2342
|
+
_onUpdate() {
|
|
2343
|
+
}
|
|
2344
|
+
};
|
|
2345
|
+
|
|
2346
|
+
// ../core/src/controllers/transition-manager.ts
|
|
2347
|
+
var noop = () => {
|
|
2348
|
+
};
|
|
2349
|
+
var PRESERVE_CONSTRAINT_CONTEXT = { mode: "preserve" };
|
|
2350
|
+
var HARD_CONSTRAINT_CONTEXT = { mode: "hard" };
|
|
2351
|
+
var TRANSITION_EVENTS = {
|
|
2352
|
+
BREAK: 1,
|
|
2353
|
+
SNAP_TO_END: 2,
|
|
2354
|
+
IGNORE: 3
|
|
2355
|
+
};
|
|
2356
|
+
var DEFAULT_EASING = (t) => t;
|
|
2357
|
+
var DEFAULT_INTERRUPTION = TRANSITION_EVENTS.BREAK;
|
|
2358
|
+
var TransitionManager = class {
|
|
2359
|
+
constructor(opts) {
|
|
2360
|
+
this._onTransitionUpdate = (transition) => {
|
|
2361
|
+
const {
|
|
2362
|
+
time,
|
|
2363
|
+
settings: { interpolator, startProps, endProps, duration, easing }
|
|
2364
|
+
} = transition;
|
|
2365
|
+
const t = easing(time / duration);
|
|
2366
|
+
const viewport = interpolator.interpolateProps(startProps, endProps, t);
|
|
2367
|
+
this.propsInTransition = this.getControllerState(
|
|
2368
|
+
{
|
|
2369
|
+
...this.props,
|
|
2370
|
+
...viewport
|
|
2371
|
+
},
|
|
2372
|
+
PRESERVE_CONSTRAINT_CONTEXT
|
|
2373
|
+
).getViewportProps();
|
|
2374
|
+
this.onViewStateChange({
|
|
2375
|
+
viewState: this.propsInTransition,
|
|
2376
|
+
oldViewState: this.props
|
|
2377
|
+
});
|
|
2378
|
+
};
|
|
2379
|
+
this.getControllerState = opts.getControllerState;
|
|
2380
|
+
this.propsInTransition = null;
|
|
2381
|
+
this.transition = new Transition(opts.timeline);
|
|
2382
|
+
this.onViewStateChange = opts.onViewStateChange || noop;
|
|
2383
|
+
this.onStateChange = opts.onStateChange || noop;
|
|
2384
|
+
}
|
|
2385
|
+
finalize() {
|
|
2386
|
+
this.transition.cancel();
|
|
2387
|
+
}
|
|
2388
|
+
// Returns current transitioned viewport.
|
|
2389
|
+
getViewportInTransition() {
|
|
2390
|
+
return this.propsInTransition;
|
|
2391
|
+
}
|
|
2392
|
+
// Process the vewiport change, either ignore or trigger a new transition.
|
|
2393
|
+
// Return true if a new transition is triggered, false otherwise.
|
|
2394
|
+
processViewStateChange(nextProps) {
|
|
2395
|
+
let transitionTriggered = false;
|
|
2396
|
+
const currentProps = this.props;
|
|
2397
|
+
this.props = nextProps;
|
|
2398
|
+
if (!currentProps || this._shouldIgnoreViewportChange(currentProps, nextProps)) {
|
|
2399
|
+
return false;
|
|
2400
|
+
}
|
|
2401
|
+
if (this._isTransitionEnabled(nextProps)) {
|
|
2402
|
+
let startProps = currentProps;
|
|
2403
|
+
if (this.transition.inProgress) {
|
|
2404
|
+
const { interruption, endProps } = this.transition.settings;
|
|
2405
|
+
startProps = {
|
|
2406
|
+
...currentProps,
|
|
2407
|
+
...interruption === TRANSITION_EVENTS.SNAP_TO_END ? endProps : this.propsInTransition || currentProps
|
|
2408
|
+
};
|
|
2409
|
+
}
|
|
2410
|
+
this._triggerTransition(startProps, nextProps);
|
|
2411
|
+
transitionTriggered = true;
|
|
2412
|
+
} else {
|
|
2413
|
+
this.transition.cancel();
|
|
2414
|
+
}
|
|
2415
|
+
return transitionTriggered;
|
|
2416
|
+
}
|
|
2417
|
+
updateTransition() {
|
|
2418
|
+
this.transition.update();
|
|
2419
|
+
}
|
|
2420
|
+
// Helper methods
|
|
2421
|
+
_isTransitionEnabled(props) {
|
|
2422
|
+
const { transitionDuration, transitionInterpolator } = props;
|
|
2423
|
+
return (transitionDuration > 0 || transitionDuration === "auto") && Boolean(transitionInterpolator);
|
|
2424
|
+
}
|
|
2425
|
+
_isUpdateDueToCurrentTransition(props) {
|
|
2426
|
+
if (this.transition.inProgress && this.propsInTransition) {
|
|
2427
|
+
return this.transition.settings.interpolator.arePropsEqual(
|
|
2428
|
+
props,
|
|
2429
|
+
this.propsInTransition
|
|
2430
|
+
);
|
|
2431
|
+
}
|
|
2432
|
+
return false;
|
|
2433
|
+
}
|
|
2434
|
+
_shouldIgnoreViewportChange(currentProps, nextProps) {
|
|
2435
|
+
if (this.transition.inProgress) {
|
|
2436
|
+
const transitionSettings = this.transition.settings;
|
|
2437
|
+
return transitionSettings.interruption === TRANSITION_EVENTS.IGNORE || // Ignore update if it is due to current active transition.
|
|
2438
|
+
this._isUpdateDueToCurrentTransition(nextProps);
|
|
2439
|
+
}
|
|
2440
|
+
if (this._isTransitionEnabled(nextProps)) {
|
|
2441
|
+
return nextProps.transitionInterpolator.arePropsEqual(
|
|
2442
|
+
currentProps,
|
|
2443
|
+
nextProps
|
|
2444
|
+
);
|
|
2445
|
+
}
|
|
2446
|
+
return true;
|
|
2447
|
+
}
|
|
2448
|
+
_triggerTransition(startProps, endProps) {
|
|
2449
|
+
const startViewstate = this.getControllerState(startProps, PRESERVE_CONSTRAINT_CONTEXT);
|
|
2450
|
+
const endViewStateProps = this.getControllerState(
|
|
2451
|
+
endProps,
|
|
2452
|
+
HARD_CONSTRAINT_CONTEXT
|
|
2453
|
+
).shortestPathFrom(startViewstate);
|
|
2454
|
+
const transitionInterpolator = endProps.transitionInterpolator;
|
|
2455
|
+
const duration = transitionInterpolator.getDuration ? transitionInterpolator.getDuration(startProps, endProps) : endProps.transitionDuration;
|
|
2456
|
+
if (duration === 0) {
|
|
2457
|
+
return;
|
|
2458
|
+
}
|
|
2459
|
+
const initialProps = transitionInterpolator.initializeProps(startProps, endViewStateProps);
|
|
2460
|
+
this.propsInTransition = {};
|
|
2461
|
+
const transitionSettings = {
|
|
2462
|
+
duration,
|
|
2463
|
+
easing: endProps.transitionEasing || DEFAULT_EASING,
|
|
2464
|
+
interpolator: transitionInterpolator,
|
|
2465
|
+
interruption: endProps.transitionInterruption || DEFAULT_INTERRUPTION,
|
|
2466
|
+
startProps: initialProps.start,
|
|
2467
|
+
endProps: initialProps.end,
|
|
2468
|
+
onStart: endProps.onTransitionStart,
|
|
2469
|
+
onUpdate: this._onTransitionUpdate,
|
|
2470
|
+
onInterrupt: this._onTransitionEnd(endProps.onTransitionInterrupt),
|
|
2471
|
+
onEnd: this._onTransitionEnd(endProps.onTransitionEnd)
|
|
2472
|
+
};
|
|
2473
|
+
this.transition.start(transitionSettings);
|
|
2474
|
+
this.onStateChange({ inTransition: true });
|
|
2475
|
+
this.updateTransition();
|
|
2476
|
+
}
|
|
2477
|
+
_onTransitionEnd(callback) {
|
|
2478
|
+
return (transition) => {
|
|
2479
|
+
this.propsInTransition = null;
|
|
2480
|
+
this.onStateChange({
|
|
2481
|
+
inTransition: false,
|
|
2482
|
+
isZooming: false,
|
|
2483
|
+
isPanning: false,
|
|
2484
|
+
isRotating: false
|
|
2485
|
+
});
|
|
2486
|
+
callback == null ? void 0 : callback(transition);
|
|
2487
|
+
};
|
|
2488
|
+
}
|
|
2489
|
+
};
|
|
2490
|
+
|
|
2491
|
+
// ../core/src/transitions/transition-interpolator.ts
|
|
2492
|
+
var import_core5 = require("@math.gl/core");
|
|
2493
|
+
|
|
2494
|
+
// ../core/src/utils/assert.ts
|
|
2495
|
+
function assert(condition, message) {
|
|
2496
|
+
if (!condition) {
|
|
2497
|
+
throw new Error(message || "deck.gl: assertion failed.");
|
|
2498
|
+
}
|
|
2499
|
+
}
|
|
2500
|
+
|
|
2501
|
+
// ../core/src/transitions/transition-interpolator.ts
|
|
2502
|
+
var TransitionInterpolator = class {
|
|
2503
|
+
/**
|
|
2504
|
+
* @param opts {array|object}
|
|
2505
|
+
* @param opts.compare {array} - prop names used in equality check
|
|
2506
|
+
* @param opts.extract {array} - prop names needed for interpolation
|
|
2507
|
+
* @param opts.required {array} - prop names that must be supplied
|
|
2508
|
+
* alternatively, supply one list of prop names as `opts` if all of the above are the same.
|
|
2509
|
+
*/
|
|
2510
|
+
constructor(opts) {
|
|
2511
|
+
const { compare, extract, required } = opts;
|
|
2512
|
+
this._propsToCompare = compare;
|
|
2513
|
+
this._propsToExtract = extract || compare;
|
|
2514
|
+
this._requiredProps = required;
|
|
2515
|
+
}
|
|
2516
|
+
/**
|
|
2517
|
+
* Checks if two sets of props need transition in between
|
|
2518
|
+
* @param currentProps {object} - a list of viewport props
|
|
2519
|
+
* @param nextProps {object} - a list of viewport props
|
|
2520
|
+
* @returns {bool} - true if two props are equivalent
|
|
2521
|
+
*/
|
|
2522
|
+
arePropsEqual(currentProps, nextProps) {
|
|
2523
|
+
for (const key of this._propsToCompare) {
|
|
2524
|
+
if (!(key in currentProps) || !(key in nextProps) || !(0, import_core5.equals)(currentProps[key], nextProps[key])) {
|
|
2525
|
+
return false;
|
|
2526
|
+
}
|
|
2527
|
+
}
|
|
2528
|
+
return true;
|
|
2529
|
+
}
|
|
2530
|
+
/**
|
|
2531
|
+
* Called before transition starts to validate/pre-process start and end props
|
|
2532
|
+
* @param startProps {object} - a list of starting viewport props
|
|
2533
|
+
* @param endProps {object} - a list of target viewport props
|
|
2534
|
+
* @returns {Object} {start, end} - start and end props to be passed
|
|
2535
|
+
* to `interpolateProps`
|
|
2536
|
+
*/
|
|
2537
|
+
initializeProps(startProps, endProps) {
|
|
2538
|
+
const startViewStateProps = {};
|
|
2539
|
+
const endViewStateProps = {};
|
|
2540
|
+
for (const key of this._propsToExtract) {
|
|
2541
|
+
if (key in startProps || key in endProps) {
|
|
2542
|
+
startViewStateProps[key] = startProps[key];
|
|
2543
|
+
endViewStateProps[key] = endProps[key];
|
|
2544
|
+
}
|
|
2545
|
+
}
|
|
2546
|
+
this._checkRequiredProps(startViewStateProps);
|
|
2547
|
+
this._checkRequiredProps(endViewStateProps);
|
|
2548
|
+
return { start: startViewStateProps, end: endViewStateProps };
|
|
2549
|
+
}
|
|
2550
|
+
/**
|
|
2551
|
+
* Returns transition duration
|
|
2552
|
+
* @param startProps {object} - a list of starting viewport props
|
|
2553
|
+
* @param endProps {object} - a list of target viewport props
|
|
2554
|
+
* @returns {Number} - transition duration in milliseconds
|
|
2555
|
+
*/
|
|
2556
|
+
getDuration(startProps, endProps) {
|
|
2557
|
+
return endProps.transitionDuration;
|
|
2558
|
+
}
|
|
2559
|
+
_checkRequiredProps(props) {
|
|
2560
|
+
if (!this._requiredProps) {
|
|
2561
|
+
return;
|
|
2562
|
+
}
|
|
2563
|
+
this._requiredProps.forEach((propName) => {
|
|
2564
|
+
const value = props[propName];
|
|
2565
|
+
assert(
|
|
2566
|
+
Number.isFinite(value) || Array.isArray(value),
|
|
2567
|
+
`${propName} is required for transition`
|
|
2568
|
+
);
|
|
2569
|
+
});
|
|
2570
|
+
}
|
|
2571
|
+
};
|
|
2572
|
+
|
|
2573
|
+
// ../core/src/transitions/linear-interpolator.ts
|
|
2574
|
+
var import_core8 = require("@math.gl/core");
|
|
2575
|
+
|
|
2576
|
+
// ../core/src/viewports/globe-viewport.ts
|
|
2577
|
+
var import_core6 = require("@math.gl/core");
|
|
2578
|
+
var import_web_mercator3 = require("@math.gl/web-mercator");
|
|
2579
|
+
var import_core7 = require("@math.gl/core");
|
|
2580
|
+
var import_web_mercator4 = require("@math.gl/web-mercator");
|
|
2581
|
+
var DEGREES_TO_RADIANS2 = Math.PI / 180;
|
|
2582
|
+
var RADIANS_TO_DEGREES = 180 / Math.PI;
|
|
2583
|
+
var EARTH_RADIUS = 6370972;
|
|
2584
|
+
var GLOBE_RADIUS = 256;
|
|
2585
|
+
function getDistanceScales2() {
|
|
2586
|
+
const unitsPerMeter2 = GLOBE_RADIUS / EARTH_RADIUS;
|
|
2587
|
+
const unitsPerDegree = Math.PI / 180 * GLOBE_RADIUS;
|
|
2588
|
+
return {
|
|
2589
|
+
unitsPerMeter: [unitsPerMeter2, unitsPerMeter2, unitsPerMeter2],
|
|
2590
|
+
unitsPerMeter2: [0, 0, 0],
|
|
2591
|
+
metersPerUnit: [1 / unitsPerMeter2, 1 / unitsPerMeter2, 1 / unitsPerMeter2],
|
|
2592
|
+
unitsPerDegree: [unitsPerDegree, unitsPerDegree, unitsPerMeter2],
|
|
2593
|
+
unitsPerDegree2: [0, 0, 0],
|
|
2594
|
+
degreesPerUnit: [1 / unitsPerDegree, 1 / unitsPerDegree, 1 / unitsPerMeter2]
|
|
2595
|
+
};
|
|
2596
|
+
}
|
|
2597
|
+
var GlobeViewport = class extends Viewport {
|
|
2598
|
+
constructor(opts = {}) {
|
|
2599
|
+
const {
|
|
2600
|
+
longitude = 0,
|
|
2601
|
+
bearing = 0,
|
|
2602
|
+
pitch = 0,
|
|
2603
|
+
zoom = 0,
|
|
2604
|
+
// Matches Maplibre defaults
|
|
2605
|
+
// https://github.com/maplibre/maplibre-gl-js/blob/f8ab4b48d59ab8fe7b068b102538793bbdd4c848/src/geo/projection/globe_transform.ts#L632-L633
|
|
2606
|
+
nearZMultiplier = 0.5,
|
|
2607
|
+
farZMultiplier = 1,
|
|
2608
|
+
resolution = 10
|
|
2609
|
+
} = opts;
|
|
2610
|
+
let { latitude = 0, height, altitude = 1.5, fovy } = opts;
|
|
2611
|
+
latitude = Math.max(Math.min(latitude, 90), -90);
|
|
2612
|
+
height = height || 1;
|
|
2613
|
+
if (fovy) {
|
|
2614
|
+
altitude = (0, import_web_mercator3.fovyToAltitude)(fovy);
|
|
2615
|
+
} else {
|
|
2616
|
+
fovy = (0, import_web_mercator3.altitudeToFovy)(altitude);
|
|
2617
|
+
}
|
|
2618
|
+
const scaleLatitude = Math.max(Math.min(latitude, import_web_mercator4.MAX_LATITUDE), -import_web_mercator4.MAX_LATITUDE);
|
|
2619
|
+
const scale = Math.pow(2, zoom - zoomAdjust(scaleLatitude));
|
|
2620
|
+
const pitchRadians = pitch * DEGREES_TO_RADIANS2;
|
|
2621
|
+
const nearZ = opts.nearZ ?? nearZMultiplier;
|
|
2622
|
+
const farZ = opts.farZ ?? (altitude + GLOBE_RADIUS * 2 * scale / height / Math.max(Math.cos(pitchRadians), 0.1)) * farZMultiplier;
|
|
2623
|
+
const viewMatrix = new import_core6.Matrix4().lookAt({ eye: [0, -altitude, 0], up: [0, 0, 1] }).rotateX(-pitchRadians).rotateY(-bearing * DEGREES_TO_RADIANS2).rotateX(latitude * DEGREES_TO_RADIANS2).rotateZ(-longitude * DEGREES_TO_RADIANS2).scale(scale / height);
|
|
2624
|
+
super({
|
|
2625
|
+
...opts,
|
|
2626
|
+
// x, y, width,
|
|
2627
|
+
height,
|
|
2628
|
+
// view matrix
|
|
2629
|
+
viewMatrix,
|
|
2630
|
+
longitude,
|
|
2631
|
+
latitude,
|
|
2632
|
+
zoom,
|
|
2633
|
+
// projection matrix parameters
|
|
2634
|
+
distanceScales: getDistanceScales2(),
|
|
2635
|
+
fovy,
|
|
2636
|
+
focalDistance: altitude,
|
|
2637
|
+
near: nearZ,
|
|
2638
|
+
far: farZ
|
|
2639
|
+
});
|
|
2640
|
+
this.scale = scale;
|
|
2641
|
+
this.latitude = latitude;
|
|
2642
|
+
this.longitude = longitude;
|
|
2643
|
+
this.bearing = bearing;
|
|
2644
|
+
this.pitch = pitch;
|
|
2645
|
+
this.fovy = fovy;
|
|
2646
|
+
this.resolution = resolution;
|
|
2647
|
+
}
|
|
2648
|
+
get projectionMode() {
|
|
2649
|
+
return PROJECTION_MODE.GLOBE;
|
|
2650
|
+
}
|
|
2651
|
+
getDistanceScales() {
|
|
2652
|
+
return this.distanceScales;
|
|
2653
|
+
}
|
|
2654
|
+
getBounds(options = {}) {
|
|
2655
|
+
const unprojectOption = { targetZ: options.z || 0 };
|
|
2656
|
+
const left = this.unproject([0, this.height / 2], unprojectOption);
|
|
2657
|
+
const top = this.unproject([this.width / 2, 0], unprojectOption);
|
|
2658
|
+
const right = this.unproject([this.width, this.height / 2], unprojectOption);
|
|
2659
|
+
const bottom = this.unproject([this.width / 2, this.height], unprojectOption);
|
|
2660
|
+
if (right[0] < this.longitude) right[0] += 360;
|
|
2661
|
+
if (left[0] > this.longitude) left[0] -= 360;
|
|
2662
|
+
return [
|
|
2663
|
+
Math.min(left[0], right[0], top[0], bottom[0]),
|
|
2664
|
+
Math.min(left[1], right[1], top[1], bottom[1]),
|
|
2665
|
+
Math.max(left[0], right[0], top[0], bottom[0]),
|
|
2666
|
+
Math.max(left[1], right[1], top[1], bottom[1])
|
|
2667
|
+
];
|
|
2668
|
+
}
|
|
2669
|
+
unproject(xyz, { topLeft = true, targetZ } = {}) {
|
|
2670
|
+
const [x, y, z] = xyz;
|
|
2671
|
+
const y2 = topLeft ? y : this.height - y;
|
|
2672
|
+
const { pixelUnprojectionMatrix } = this;
|
|
2673
|
+
let coord;
|
|
2674
|
+
if (Number.isFinite(z)) {
|
|
2675
|
+
coord = transformVector(pixelUnprojectionMatrix, [x, y2, z, 1]);
|
|
2676
|
+
} else {
|
|
2677
|
+
const coord0 = transformVector(pixelUnprojectionMatrix, [x, y2, -1, 1]);
|
|
2678
|
+
const coord1 = transformVector(pixelUnprojectionMatrix, [x, y2, 1, 1]);
|
|
2679
|
+
const lt = ((targetZ || 0) / EARTH_RADIUS + 1) * GLOBE_RADIUS;
|
|
2680
|
+
const lSqr = import_core7.vec3.sqrLen(import_core7.vec3.sub([], coord0, coord1));
|
|
2681
|
+
const l0Sqr = import_core7.vec3.sqrLen(coord0);
|
|
2682
|
+
const l1Sqr = import_core7.vec3.sqrLen(coord1);
|
|
2683
|
+
const sSqr = (4 * l0Sqr * l1Sqr - (lSqr - l0Sqr - l1Sqr) ** 2) / 16;
|
|
2684
|
+
const dSqr = 4 * sSqr / lSqr;
|
|
2685
|
+
const r0 = Math.sqrt(l0Sqr - dSqr);
|
|
2686
|
+
const dr = Math.sqrt(Math.max(0, lt * lt - dSqr));
|
|
2687
|
+
const t = (r0 - dr) / Math.sqrt(lSqr);
|
|
2688
|
+
coord = import_core7.vec3.lerp([], coord0, coord1, t);
|
|
2689
|
+
}
|
|
2690
|
+
const [X, Y, Z] = this.unprojectPosition(coord);
|
|
2691
|
+
if (Number.isFinite(z)) {
|
|
2692
|
+
return [X, Y, Z];
|
|
2693
|
+
}
|
|
2694
|
+
return Number.isFinite(targetZ) ? [X, Y, targetZ] : [X, Y];
|
|
2695
|
+
}
|
|
2696
|
+
projectPosition(xyz) {
|
|
2697
|
+
const [lng, lat, Z = 0] = xyz;
|
|
2698
|
+
const lambda = lng * DEGREES_TO_RADIANS2;
|
|
2699
|
+
const phi = lat * DEGREES_TO_RADIANS2;
|
|
2700
|
+
const cosPhi = Math.cos(phi);
|
|
2701
|
+
const D = (Z / EARTH_RADIUS + 1) * GLOBE_RADIUS;
|
|
2702
|
+
return [Math.sin(lambda) * cosPhi * D, -Math.cos(lambda) * cosPhi * D, Math.sin(phi) * D];
|
|
2703
|
+
}
|
|
2704
|
+
unprojectPosition(xyz) {
|
|
2705
|
+
const [x, y, z] = xyz;
|
|
2706
|
+
const D = import_core7.vec3.len(xyz);
|
|
2707
|
+
const phi = Math.asin(z / D);
|
|
2708
|
+
const lambda = Math.atan2(x, -y);
|
|
2709
|
+
const lng = lambda * RADIANS_TO_DEGREES;
|
|
2710
|
+
const lat = phi * RADIANS_TO_DEGREES;
|
|
2711
|
+
const Z = (D / GLOBE_RADIUS - 1) * EARTH_RADIUS;
|
|
2712
|
+
return [lng, lat, Z];
|
|
2713
|
+
}
|
|
2714
|
+
projectFlat(xyz) {
|
|
2715
|
+
return xyz;
|
|
2716
|
+
}
|
|
2717
|
+
unprojectFlat(xyz) {
|
|
2718
|
+
return xyz;
|
|
2719
|
+
}
|
|
2720
|
+
/**
|
|
2721
|
+
* Pan the globe using delta-based movement
|
|
2722
|
+
* @param coords - the geographic coordinates where the pan started
|
|
2723
|
+
* @param pixel - the current screen position
|
|
2724
|
+
* @param startPixel - the screen position where the pan started
|
|
2725
|
+
* @returns updated viewport options with new longitude/latitude
|
|
2726
|
+
*/
|
|
2727
|
+
panByPosition([startLng, startLat, startZoom], pixel, startPixel) {
|
|
2728
|
+
const scale = Math.pow(2, this.zoom - zoomAdjust(this.latitude));
|
|
2729
|
+
const rotationSpeed = 0.25 / scale;
|
|
2730
|
+
const longitude = startLng + rotationSpeed * (startPixel[0] - pixel[0]);
|
|
2731
|
+
let latitude = startLat - rotationSpeed * (startPixel[1] - pixel[1]);
|
|
2732
|
+
latitude = Math.max(Math.min(latitude, 90), -90);
|
|
2733
|
+
const out = { longitude, latitude, zoom: startZoom - zoomAdjust(startLat) };
|
|
2734
|
+
out.zoom += zoomAdjust(out.latitude);
|
|
2735
|
+
return out;
|
|
2736
|
+
}
|
|
2737
|
+
};
|
|
2738
|
+
GlobeViewport.displayName = "GlobeViewport";
|
|
2739
|
+
function zoomAdjust(latitude, clampToPoles) {
|
|
2740
|
+
if (clampToPoles) {
|
|
2741
|
+
latitude = Math.max(Math.min(latitude, import_web_mercator4.MAX_LATITUDE), -import_web_mercator4.MAX_LATITUDE);
|
|
2742
|
+
}
|
|
2743
|
+
const scaleAdjust = Math.PI * Math.cos(latitude * Math.PI / 180);
|
|
2744
|
+
return Math.log2(scaleAdjust);
|
|
2745
|
+
}
|
|
2746
|
+
function transformVector(matrix, vector) {
|
|
2747
|
+
const result = import_core7.vec4.transformMat4([], vector, matrix);
|
|
2748
|
+
import_core7.vec4.scale(result, result, 1 / result[3]);
|
|
2749
|
+
return result;
|
|
2750
|
+
}
|
|
2751
|
+
|
|
2752
|
+
// ../core/src/transitions/linear-interpolator.ts
|
|
2753
|
+
var DEFAULT_PROPS = ["longitude", "latitude", "zoom", "bearing", "pitch"];
|
|
2754
|
+
var DEFAULT_REQUIRED_PROPS = ["longitude", "latitude", "zoom"];
|
|
2755
|
+
var LinearInterpolator = class extends TransitionInterpolator {
|
|
2756
|
+
/**
|
|
2757
|
+
* @param {Object} opts
|
|
2758
|
+
* @param {Array} opts.transitionProps - list of props to apply linear transition to.
|
|
2759
|
+
* @param {Array} opts.around - a screen point to zoom/rotate around.
|
|
2760
|
+
* @param {Function} opts.makeViewport - construct a viewport instance with given props.
|
|
2761
|
+
*/
|
|
2762
|
+
constructor(opts = {}) {
|
|
2763
|
+
const transitionProps = Array.isArray(opts) ? opts : opts.transitionProps;
|
|
2764
|
+
const normalizedOpts = Array.isArray(opts) ? {} : opts;
|
|
2765
|
+
normalizedOpts.transitionProps = Array.isArray(transitionProps) ? {
|
|
2766
|
+
compare: transitionProps,
|
|
2767
|
+
required: transitionProps
|
|
2768
|
+
} : transitionProps || {
|
|
2769
|
+
compare: DEFAULT_PROPS,
|
|
2770
|
+
required: DEFAULT_REQUIRED_PROPS
|
|
2771
|
+
};
|
|
2772
|
+
super(normalizedOpts.transitionProps);
|
|
2773
|
+
this.opts = normalizedOpts;
|
|
2774
|
+
}
|
|
2775
|
+
initializeProps(startProps, endProps) {
|
|
2776
|
+
const result = super.initializeProps(startProps, endProps);
|
|
2777
|
+
const { makeViewport, around } = this.opts;
|
|
2778
|
+
if (makeViewport && around) {
|
|
2779
|
+
const TestViewport = makeViewport(startProps);
|
|
2780
|
+
if (TestViewport instanceof GlobeViewport) {
|
|
2781
|
+
log_default.warn("around not supported in GlobeView")();
|
|
2782
|
+
} else {
|
|
2783
|
+
const startViewport = makeViewport(startProps);
|
|
2784
|
+
const endViewport = makeViewport(endProps);
|
|
2785
|
+
const aroundPosition = startViewport.unproject(around);
|
|
2786
|
+
result.start.around = around;
|
|
2787
|
+
Object.assign(result.end, {
|
|
2788
|
+
around: endViewport.project(aroundPosition),
|
|
2789
|
+
aroundPosition,
|
|
2790
|
+
width: endProps.width,
|
|
2791
|
+
height: endProps.height
|
|
2792
|
+
});
|
|
2793
|
+
}
|
|
2794
|
+
}
|
|
2795
|
+
return result;
|
|
2796
|
+
}
|
|
2797
|
+
interpolateProps(startProps, endProps, t) {
|
|
2798
|
+
const propsInTransition = {};
|
|
2799
|
+
for (const key of this._propsToExtract) {
|
|
2800
|
+
propsInTransition[key] = (0, import_core8.lerp)(startProps[key] || 0, endProps[key] || 0, t);
|
|
2801
|
+
}
|
|
2802
|
+
if (endProps.aroundPosition && this.opts.makeViewport) {
|
|
2803
|
+
const viewport = this.opts.makeViewport({ ...endProps, ...propsInTransition });
|
|
2804
|
+
Object.assign(
|
|
2805
|
+
propsInTransition,
|
|
2806
|
+
viewport.panByPosition(
|
|
2807
|
+
endProps.aroundPosition,
|
|
2808
|
+
// anchor point in current screen coordinates
|
|
2809
|
+
(0, import_core8.lerp)(startProps.around, endProps.around, t)
|
|
2810
|
+
)
|
|
2811
|
+
);
|
|
2812
|
+
}
|
|
2813
|
+
return propsInTransition;
|
|
2814
|
+
}
|
|
2815
|
+
};
|
|
2816
|
+
|
|
2817
|
+
// ../core/src/controllers/controller.ts
|
|
2818
|
+
var NO_TRANSITION_PROPS = {
|
|
2819
|
+
transitionDuration: 0
|
|
2820
|
+
};
|
|
2821
|
+
var DEFAULT_INERTIA = 300;
|
|
2822
|
+
var REBOUND_DURATION = 300;
|
|
2823
|
+
var INERTIA_EASING = (t) => 1 - (1 - t) * (1 - t);
|
|
2824
|
+
var EASE_OUT_EXPONENTIAL = (t) => t === 1 ? 1 : 1 - Math.pow(2, -10 * t);
|
|
2825
|
+
var EVENT_TYPES = {
|
|
2826
|
+
WHEEL: ["wheel"],
|
|
2827
|
+
PAN: ["panstart", "panmove", "panend"],
|
|
2828
|
+
PINCH: ["pinchstart", "pinchmove", "pinchend"],
|
|
2829
|
+
MULTI_PAN: ["multipanstart", "multipanmove", "multipanend"],
|
|
2830
|
+
DOUBLE_CLICK: ["dblclick"],
|
|
2831
|
+
DOUBLE_CLICK_DRAG: [
|
|
2832
|
+
"dblclickdragstart",
|
|
2833
|
+
"dblclickdragmove",
|
|
2834
|
+
"dblclickdragend",
|
|
2835
|
+
"dblclickdragcancel"
|
|
2836
|
+
],
|
|
2837
|
+
KEYBOARD: ["keydown"]
|
|
2838
|
+
};
|
|
2839
|
+
var pinchEventWorkaround = {};
|
|
2840
|
+
var Controller = class {
|
|
2841
|
+
constructor(opts) {
|
|
2842
|
+
this.state = {};
|
|
2843
|
+
this._events = {};
|
|
2844
|
+
this._interactionState = {
|
|
2845
|
+
isDragging: false
|
|
2846
|
+
};
|
|
2847
|
+
this._customEvents = [];
|
|
2848
|
+
this._eventStartBlocked = null;
|
|
2849
|
+
this._panMove = false;
|
|
2850
|
+
this._multiPanMode = null;
|
|
2851
|
+
this._multiPanStartCenter = null;
|
|
2852
|
+
this._doubleClickDragAnchor = null;
|
|
2853
|
+
this._suppressDoubleClickUntil = 0;
|
|
2854
|
+
this.invertPan = false;
|
|
2855
|
+
this.dragMode = "rotate";
|
|
2856
|
+
this.inertia = 0;
|
|
2857
|
+
this.scrollZoom = true;
|
|
2858
|
+
this.dragPan = true;
|
|
2859
|
+
this.dragRotate = true;
|
|
2860
|
+
this.doubleClickZoom = true;
|
|
2861
|
+
this.doubleClickDragZoom = true;
|
|
2862
|
+
this.touchZoom = true;
|
|
2863
|
+
this.touchRotate = false;
|
|
2864
|
+
this.multiTouchDrag = null;
|
|
2865
|
+
this.trackpadGesture = false;
|
|
2866
|
+
this.keyboard = true;
|
|
2867
|
+
this.transitionManager = new TransitionManager({
|
|
2868
|
+
...opts,
|
|
2869
|
+
getControllerState: (props, constraintContext) => new this.ControllerState({
|
|
2870
|
+
...props,
|
|
2871
|
+
constraintContext,
|
|
2872
|
+
makeViewport: opts.makeViewport
|
|
2873
|
+
}),
|
|
2874
|
+
onViewStateChange: this._onTransition.bind(this),
|
|
2875
|
+
onStateChange: this._setInteractionState.bind(this)
|
|
2876
|
+
});
|
|
2877
|
+
this.handleEvent = this.handleEvent.bind(this);
|
|
2878
|
+
this.eventManager = opts.eventManager;
|
|
2879
|
+
this.onViewStateChange = opts.onViewStateChange || (() => {
|
|
2880
|
+
});
|
|
2881
|
+
this.onStateChange = opts.onStateChange || (() => {
|
|
2882
|
+
});
|
|
2883
|
+
this.makeViewport = opts.makeViewport;
|
|
2884
|
+
this.pickPosition = opts.pickPosition;
|
|
2885
|
+
}
|
|
2886
|
+
set events(customEvents) {
|
|
2887
|
+
this.toggleEvents(this._customEvents, false);
|
|
2888
|
+
this.toggleEvents(customEvents, true);
|
|
2889
|
+
this._customEvents = customEvents;
|
|
2890
|
+
if (this.props) {
|
|
2891
|
+
this.setProps(this.props);
|
|
2892
|
+
}
|
|
2893
|
+
}
|
|
2894
|
+
finalize() {
|
|
2895
|
+
var _a2;
|
|
2896
|
+
for (const eventName in this._events) {
|
|
2897
|
+
if (this._events[eventName]) {
|
|
2898
|
+
(_a2 = this.eventManager) == null ? void 0 : _a2.off(eventName, this.handleEvent);
|
|
2899
|
+
}
|
|
2900
|
+
}
|
|
2901
|
+
this.transitionManager.finalize();
|
|
2902
|
+
}
|
|
2903
|
+
/**
|
|
2904
|
+
* Callback for events
|
|
2905
|
+
*/
|
|
2906
|
+
handleEvent(event) {
|
|
2907
|
+
this._controllerState = void 0;
|
|
2908
|
+
const eventStartBlocked = this._eventStartBlocked;
|
|
2909
|
+
switch (event.type) {
|
|
2910
|
+
case "panstart":
|
|
2911
|
+
return eventStartBlocked ? false : this._onPanStart(event);
|
|
2912
|
+
case "panmove":
|
|
2913
|
+
return this._onPan(event);
|
|
2914
|
+
case "panend":
|
|
2915
|
+
return this._onPanEnd(event);
|
|
2916
|
+
case "pinchstart":
|
|
2917
|
+
return eventStartBlocked || !this._isTrackpadGestureAllowed(event) ? false : this._onPinchStart(event);
|
|
2918
|
+
case "pinchmove":
|
|
2919
|
+
return this._isTrackpadGestureAllowed(event) ? this._onPinch(event) : false;
|
|
2920
|
+
case "pinchend":
|
|
2921
|
+
return this._isTrackpadGestureAllowed(event) ? this._onPinchEnd(event) : false;
|
|
2922
|
+
case "multipanstart":
|
|
2923
|
+
return eventStartBlocked ? false : this._onMultiPanStart(event);
|
|
2924
|
+
case "multipanmove":
|
|
2925
|
+
return this._onMultiPan(event);
|
|
2926
|
+
case "multipanend":
|
|
2927
|
+
return this._onMultiPanEnd(event);
|
|
2928
|
+
case "dblclick":
|
|
2929
|
+
return this._onDoubleClick(event);
|
|
2930
|
+
case "dblclickdragstart":
|
|
2931
|
+
return eventStartBlocked ? false : this._onDoubleClickDragStart(event);
|
|
2932
|
+
case "dblclickdragmove":
|
|
2933
|
+
return this._onDoubleClickDrag(event);
|
|
2934
|
+
case "dblclickdragend":
|
|
2935
|
+
case "dblclickdragcancel":
|
|
2936
|
+
return this._onDoubleClickDragEnd(event);
|
|
2937
|
+
case "wheel":
|
|
2938
|
+
return this._onWheel(event);
|
|
2939
|
+
case "keydown":
|
|
2940
|
+
return this._onKeyDown(event);
|
|
2941
|
+
default:
|
|
2942
|
+
return false;
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2945
|
+
/* Event utils */
|
|
2946
|
+
// Event object: http://hammerjs.github.io/api/#event-object
|
|
2947
|
+
get controllerState() {
|
|
2948
|
+
this._controllerState = this._controllerState || new this.ControllerState({
|
|
2949
|
+
makeViewport: this.makeViewport,
|
|
2950
|
+
...this.props,
|
|
2951
|
+
...this.state
|
|
2952
|
+
});
|
|
2953
|
+
return this._controllerState;
|
|
2954
|
+
}
|
|
2955
|
+
getCenter(event) {
|
|
2956
|
+
const { x, y } = this.props;
|
|
2957
|
+
const { offsetCenter } = event;
|
|
2958
|
+
return [offsetCenter.x - x, offsetCenter.y - y];
|
|
2959
|
+
}
|
|
2960
|
+
isPointInBounds(pos, event) {
|
|
2961
|
+
const { width, height } = this.props;
|
|
2962
|
+
if (event && event.handled) {
|
|
2963
|
+
return false;
|
|
2964
|
+
}
|
|
2965
|
+
const inside = pos[0] >= 0 && pos[0] <= width && pos[1] >= 0 && pos[1] <= height;
|
|
2966
|
+
if (inside && event) {
|
|
2967
|
+
event.stopPropagation();
|
|
2968
|
+
}
|
|
2969
|
+
return inside;
|
|
2970
|
+
}
|
|
2971
|
+
isFunctionKeyPressed(event) {
|
|
2972
|
+
const { srcEvent } = event;
|
|
2973
|
+
return Boolean(srcEvent.metaKey || srcEvent.altKey || srcEvent.ctrlKey || srcEvent.shiftKey);
|
|
2974
|
+
}
|
|
2975
|
+
isDragging() {
|
|
2976
|
+
return this._interactionState.isDragging || false;
|
|
2977
|
+
}
|
|
2978
|
+
// When a multi-touch event ends, e.g. pinch, not all pointers are lifted at the same time.
|
|
2979
|
+
// This triggers a brief `pan` event.
|
|
2980
|
+
// Calling this method will temporarily disable *start events to avoid conflicting transitions.
|
|
2981
|
+
blockEvents(timeout) {
|
|
2982
|
+
const timer = setTimeout(() => {
|
|
2983
|
+
if (this._eventStartBlocked === timer) {
|
|
2984
|
+
this._eventStartBlocked = null;
|
|
2985
|
+
}
|
|
2986
|
+
}, timeout);
|
|
2987
|
+
this._eventStartBlocked = timer;
|
|
2988
|
+
}
|
|
2989
|
+
/**
|
|
2990
|
+
* Extract interactivity options
|
|
2991
|
+
*/
|
|
2992
|
+
setProps(props) {
|
|
2993
|
+
if (props.maxBoundsPadding === void 0) {
|
|
2994
|
+
props.maxBoundsPadding = null;
|
|
2995
|
+
}
|
|
2996
|
+
if (props.dragMode) {
|
|
2997
|
+
this.dragMode = props.dragMode;
|
|
2998
|
+
}
|
|
2999
|
+
const oldProps = this.props;
|
|
3000
|
+
this.props = props;
|
|
3001
|
+
if (!("transitionInterpolator" in props)) {
|
|
3002
|
+
props.transitionInterpolator = this._getTransitionProps().transitionInterpolator;
|
|
3003
|
+
}
|
|
3004
|
+
this.transitionManager.processViewStateChange(props);
|
|
3005
|
+
const { inertia } = props;
|
|
3006
|
+
this.inertia = Number.isFinite(inertia) ? inertia : inertia === true ? DEFAULT_INERTIA : 0;
|
|
3007
|
+
const {
|
|
3008
|
+
scrollZoom = true,
|
|
3009
|
+
dragPan = true,
|
|
3010
|
+
dragRotate = true,
|
|
3011
|
+
doubleClickZoom = true,
|
|
3012
|
+
doubleClickDragZoom = false,
|
|
3013
|
+
touchZoom = true,
|
|
3014
|
+
touchRotate = false,
|
|
3015
|
+
multiTouchDrag = touchRotate ? "rotate" : null,
|
|
3016
|
+
trackpadGesture = false,
|
|
3017
|
+
keyboard = true
|
|
3018
|
+
} = props;
|
|
3019
|
+
const isInteractive = Boolean(this.onViewStateChange);
|
|
3020
|
+
this.toggleEvents(EVENT_TYPES.WHEEL, isInteractive && scrollZoom);
|
|
3021
|
+
this.toggleEvents(EVENT_TYPES.PAN, isInteractive);
|
|
3022
|
+
this.toggleEvents(
|
|
3023
|
+
EVENT_TYPES.PINCH,
|
|
3024
|
+
isInteractive && (touchZoom || multiTouchDrag === "rotate")
|
|
3025
|
+
);
|
|
3026
|
+
this.toggleEvents(EVENT_TYPES.MULTI_PAN, isInteractive && Boolean(multiTouchDrag));
|
|
3027
|
+
this.toggleEvents(EVENT_TYPES.DOUBLE_CLICK, isInteractive && doubleClickZoom);
|
|
3028
|
+
this.toggleEvents(EVENT_TYPES.DOUBLE_CLICK_DRAG, isInteractive && doubleClickDragZoom);
|
|
3029
|
+
this.toggleEvents(EVENT_TYPES.KEYBOARD, isInteractive && keyboard);
|
|
3030
|
+
this.scrollZoom = scrollZoom;
|
|
3031
|
+
this.dragPan = dragPan;
|
|
3032
|
+
this.dragRotate = dragRotate;
|
|
3033
|
+
this.doubleClickZoom = doubleClickZoom;
|
|
3034
|
+
this.doubleClickDragZoom = doubleClickDragZoom;
|
|
3035
|
+
this.touchZoom = touchZoom;
|
|
3036
|
+
this.touchRotate = multiTouchDrag === "rotate";
|
|
3037
|
+
this.multiTouchDrag = multiTouchDrag;
|
|
3038
|
+
this.trackpadGesture = trackpadGesture;
|
|
3039
|
+
this.keyboard = keyboard;
|
|
3040
|
+
const constraintChanged = !oldProps || oldProps.height !== props.height || oldProps.width !== props.width || oldProps.maxBounds !== props.maxBounds || oldProps.maxBoundsPadding !== props.maxBoundsPadding;
|
|
3041
|
+
if (constraintChanged && props.maxBounds) {
|
|
3042
|
+
const controllerState = new this.ControllerState({ ...props, makeViewport: this.makeViewport });
|
|
3043
|
+
const normalizedProps = controllerState.getViewportProps();
|
|
3044
|
+
const changed = Object.keys(normalizedProps).some(
|
|
3045
|
+
(key) => !deepEqual(normalizedProps[key], props[key], 1)
|
|
3046
|
+
);
|
|
3047
|
+
if (changed) {
|
|
3048
|
+
this.updateViewport(controllerState);
|
|
3049
|
+
}
|
|
3050
|
+
}
|
|
3051
|
+
}
|
|
3052
|
+
updateTransition() {
|
|
3053
|
+
this.transitionManager.updateTransition();
|
|
3054
|
+
}
|
|
3055
|
+
toggleEvents(eventNames, enabled) {
|
|
3056
|
+
if (this.eventManager) {
|
|
3057
|
+
eventNames.forEach((eventName) => {
|
|
3058
|
+
if (this._events[eventName] !== enabled) {
|
|
3059
|
+
this._events[eventName] = enabled;
|
|
3060
|
+
if (enabled) {
|
|
3061
|
+
this.eventManager.on(eventName, this.handleEvent);
|
|
3062
|
+
} else {
|
|
3063
|
+
this.eventManager.off(eventName, this.handleEvent);
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
});
|
|
3067
|
+
}
|
|
3068
|
+
}
|
|
3069
|
+
// Private Methods
|
|
3070
|
+
/* Callback util */
|
|
3071
|
+
// formats map state and invokes callback function
|
|
3072
|
+
updateViewport(newControllerState, extraProps = null, interactionState = {}) {
|
|
3073
|
+
const viewState = { ...newControllerState.getViewportProps(), ...extraProps };
|
|
3074
|
+
const changed = this.controllerState !== newControllerState;
|
|
3075
|
+
this.state = newControllerState.getState();
|
|
3076
|
+
this._setInteractionState(interactionState);
|
|
3077
|
+
if (changed) {
|
|
3078
|
+
const oldViewState = this.controllerState && this.controllerState.getViewportProps();
|
|
3079
|
+
if (this.onViewStateChange) {
|
|
3080
|
+
this.onViewStateChange({
|
|
3081
|
+
viewState,
|
|
3082
|
+
interactionState: this._interactionState,
|
|
3083
|
+
oldViewState,
|
|
3084
|
+
viewId: this.props.id
|
|
3085
|
+
});
|
|
3086
|
+
}
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
_onTransition(params) {
|
|
3090
|
+
this.onViewStateChange({
|
|
3091
|
+
...params,
|
|
3092
|
+
interactionState: this._interactionState,
|
|
3093
|
+
viewId: this.props.id
|
|
3094
|
+
});
|
|
3095
|
+
}
|
|
3096
|
+
_setInteractionState(newStates) {
|
|
3097
|
+
Object.assign(this._interactionState, newStates);
|
|
3098
|
+
this.onStateChange(this._interactionState);
|
|
3099
|
+
}
|
|
3100
|
+
/** Maps a semantic input lifecycle to the constraint policy seen by controller state. */
|
|
3101
|
+
_getConstraintContext(_action, phase) {
|
|
3102
|
+
if (!this.props.rubberBand) {
|
|
3103
|
+
return { mode: "hard" };
|
|
3104
|
+
}
|
|
3105
|
+
return { mode: phase === "update" ? "elastic" : phase === "end" ? "rebound" : "hard" };
|
|
3106
|
+
}
|
|
3107
|
+
/** Returns a rebound transition when hard resolution changed the displayed viewport props. */
|
|
3108
|
+
_getReboundTransition(constraintContext, nextControllerState) {
|
|
3109
|
+
if (constraintContext.mode !== "rebound") {
|
|
3110
|
+
return null;
|
|
3111
|
+
}
|
|
3112
|
+
const nextViewportProps = nextControllerState.getViewportProps();
|
|
3113
|
+
const shouldRebound = Object.keys(nextViewportProps).some(
|
|
3114
|
+
(key) => !deepEqual(this.props[key], nextViewportProps[key], 1)
|
|
3115
|
+
);
|
|
3116
|
+
return shouldRebound ? {
|
|
3117
|
+
...this._getTransitionProps(),
|
|
3118
|
+
transitionDuration: REBOUND_DURATION,
|
|
3119
|
+
transitionEasing: EASE_OUT_EXPONENTIAL
|
|
3120
|
+
} : null;
|
|
3121
|
+
}
|
|
3122
|
+
/* Event handlers */
|
|
3123
|
+
// Default handler for the `panstart` event.
|
|
3124
|
+
_onPanStart(event) {
|
|
3125
|
+
const pos = this.getCenter(event);
|
|
3126
|
+
if (!this.isPointInBounds(pos, event)) {
|
|
3127
|
+
return false;
|
|
3128
|
+
}
|
|
3129
|
+
let alternateMode = this.isFunctionKeyPressed(event) || event.rightButton || false;
|
|
3130
|
+
if (this.invertPan || this.dragMode === "pan") {
|
|
3131
|
+
alternateMode = !alternateMode;
|
|
3132
|
+
}
|
|
3133
|
+
const action = alternateMode ? "pan" : "rotate";
|
|
3134
|
+
const constraintContext = this._getConstraintContext(action, "start");
|
|
3135
|
+
const newControllerState = alternateMode ? this.controllerState.panStart({ pos }, constraintContext) : this.controllerState.rotateStart({ pos }, constraintContext);
|
|
3136
|
+
this._panMove = alternateMode;
|
|
3137
|
+
this.updateViewport(newControllerState, NO_TRANSITION_PROPS, { isDragging: true });
|
|
3138
|
+
return true;
|
|
3139
|
+
}
|
|
3140
|
+
// Default handler for the `panmove` and `panend` event.
|
|
3141
|
+
_onPan(event) {
|
|
3142
|
+
if (!this.isDragging()) {
|
|
3143
|
+
return false;
|
|
3144
|
+
}
|
|
3145
|
+
return this._panMove ? this._onPanMove(event) : this._onPanRotate(event);
|
|
3146
|
+
}
|
|
3147
|
+
_onPanEnd(event) {
|
|
3148
|
+
if (!this.isDragging()) {
|
|
3149
|
+
return false;
|
|
3150
|
+
}
|
|
3151
|
+
return this._panMove ? this._onPanMoveEnd(event) : this._onPanRotateEnd(event);
|
|
3152
|
+
}
|
|
3153
|
+
// Default handler for panning to move.
|
|
3154
|
+
// Called by `_onPan` when panning without function key pressed.
|
|
3155
|
+
_onPanMove(event) {
|
|
3156
|
+
if (!this.dragPan) {
|
|
3157
|
+
return false;
|
|
3158
|
+
}
|
|
3159
|
+
const pos = this.getCenter(event);
|
|
3160
|
+
const newControllerState = this.controllerState.pan(
|
|
3161
|
+
{ pos },
|
|
3162
|
+
this._getConstraintContext("pan", "update")
|
|
3163
|
+
);
|
|
3164
|
+
this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
|
|
3165
|
+
isDragging: true,
|
|
3166
|
+
isPanning: true
|
|
3167
|
+
});
|
|
3168
|
+
return true;
|
|
3169
|
+
}
|
|
3170
|
+
_onPanMoveEnd(event) {
|
|
3171
|
+
const { inertia } = this;
|
|
3172
|
+
if (this.dragPan && inertia && event.velocity) {
|
|
3173
|
+
const pos = this.getCenter(event);
|
|
3174
|
+
const endPos = [
|
|
3175
|
+
pos[0] + event.velocityX * inertia / 2,
|
|
3176
|
+
pos[1] + event.velocityY * inertia / 2
|
|
3177
|
+
];
|
|
3178
|
+
const newControllerState = this.controllerState.pan({ pos: endPos }).panEnd();
|
|
3179
|
+
this.updateViewport(
|
|
3180
|
+
newControllerState,
|
|
3181
|
+
{
|
|
3182
|
+
...this._getTransitionProps(),
|
|
3183
|
+
transitionDuration: inertia,
|
|
3184
|
+
transitionEasing: INERTIA_EASING
|
|
3185
|
+
},
|
|
3186
|
+
{
|
|
3187
|
+
isDragging: false,
|
|
3188
|
+
isPanning: true
|
|
3189
|
+
}
|
|
3190
|
+
);
|
|
3191
|
+
} else {
|
|
3192
|
+
const currentControllerState = this.controllerState;
|
|
3193
|
+
const constraintContext = this._getConstraintContext("pan", "end");
|
|
3194
|
+
const newControllerState = currentControllerState.panEnd(constraintContext);
|
|
3195
|
+
const reboundTransition = this._getReboundTransition(constraintContext, newControllerState);
|
|
3196
|
+
this.updateViewport(newControllerState, reboundTransition, {
|
|
3197
|
+
isDragging: false,
|
|
3198
|
+
isPanning: Boolean(reboundTransition)
|
|
3199
|
+
});
|
|
3200
|
+
}
|
|
3201
|
+
return true;
|
|
3202
|
+
}
|
|
3203
|
+
// Default handler for panning to rotate.
|
|
3204
|
+
// Called by `_onPan` when panning with function key pressed.
|
|
3205
|
+
_onPanRotate(event) {
|
|
3206
|
+
if (!this.dragRotate) {
|
|
3207
|
+
return false;
|
|
3208
|
+
}
|
|
3209
|
+
const pos = this.getCenter(event);
|
|
3210
|
+
const newControllerState = this.controllerState.rotate(
|
|
3211
|
+
{ pos },
|
|
3212
|
+
this._getConstraintContext("rotate", "update")
|
|
3213
|
+
);
|
|
3214
|
+
this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
|
|
3215
|
+
isDragging: true,
|
|
3216
|
+
isRotating: true
|
|
3217
|
+
});
|
|
3218
|
+
return true;
|
|
3219
|
+
}
|
|
3220
|
+
_onPanRotateEnd(event) {
|
|
3221
|
+
const { inertia } = this;
|
|
3222
|
+
if (this.dragRotate && inertia && event.velocity) {
|
|
3223
|
+
const pos = this.getCenter(event);
|
|
3224
|
+
const endPos = [
|
|
3225
|
+
pos[0] + event.velocityX * inertia / 2,
|
|
3226
|
+
pos[1] + event.velocityY * inertia / 2
|
|
3227
|
+
];
|
|
3228
|
+
const newControllerState = this.controllerState.rotate({ pos: endPos }).rotateEnd();
|
|
3229
|
+
this.updateViewport(
|
|
3230
|
+
newControllerState,
|
|
3231
|
+
{
|
|
3232
|
+
...this._getTransitionProps(),
|
|
3233
|
+
transitionDuration: inertia,
|
|
3234
|
+
transitionEasing: INERTIA_EASING
|
|
3235
|
+
},
|
|
3236
|
+
{
|
|
3237
|
+
isDragging: false,
|
|
3238
|
+
isRotating: true
|
|
3239
|
+
}
|
|
3240
|
+
);
|
|
3241
|
+
} else {
|
|
3242
|
+
const currentControllerState = this.controllerState;
|
|
3243
|
+
const constraintContext = this._getConstraintContext("rotate", "end");
|
|
3244
|
+
const newControllerState = currentControllerState.rotateEnd(constraintContext);
|
|
3245
|
+
const reboundTransition = this._getReboundTransition(constraintContext, newControllerState);
|
|
3246
|
+
this.updateViewport(newControllerState, reboundTransition, {
|
|
3247
|
+
isDragging: false,
|
|
3248
|
+
isRotating: Boolean(reboundTransition)
|
|
3249
|
+
});
|
|
3250
|
+
}
|
|
3251
|
+
return true;
|
|
3252
|
+
}
|
|
3253
|
+
// Default handler for the `wheel` event.
|
|
3254
|
+
_onWheel(event) {
|
|
3255
|
+
if (!this.scrollZoom) {
|
|
3256
|
+
return false;
|
|
3257
|
+
}
|
|
3258
|
+
if (this.trackpadGesture && event.device !== "mouse") {
|
|
3259
|
+
return false;
|
|
3260
|
+
}
|
|
3261
|
+
const pos = this.getCenter(event);
|
|
3262
|
+
if (!this.isPointInBounds(pos, event)) {
|
|
3263
|
+
return false;
|
|
3264
|
+
}
|
|
3265
|
+
event.srcEvent.preventDefault();
|
|
3266
|
+
const { speed = 0.01, smooth = false } = this.scrollZoom === true ? {} : this.scrollZoom;
|
|
3267
|
+
const { delta } = event;
|
|
3268
|
+
let scale = 2 / (1 + Math.exp(-Math.abs(delta * speed)));
|
|
3269
|
+
if (delta < 0 && scale !== 0) {
|
|
3270
|
+
scale = 1 / scale;
|
|
3271
|
+
}
|
|
3272
|
+
const transitionProps = smooth ? { ...this._getTransitionProps({ around: pos }), transitionDuration: 250 } : NO_TRANSITION_PROPS;
|
|
3273
|
+
const newControllerState = this.controllerState.zoom({ pos, scale });
|
|
3274
|
+
this.updateViewport(newControllerState, transitionProps, {
|
|
3275
|
+
isZooming: true,
|
|
3276
|
+
isPanning: true
|
|
3277
|
+
});
|
|
3278
|
+
if (!smooth) {
|
|
3279
|
+
this._setInteractionState({ isZooming: false, isPanning: false });
|
|
3280
|
+
}
|
|
3281
|
+
return true;
|
|
3282
|
+
}
|
|
3283
|
+
_onMultiPanStart(event) {
|
|
3284
|
+
const { multiTouchDrag } = this;
|
|
3285
|
+
if (!multiTouchDrag || !this._isMultiPanEventAllowed(event, multiTouchDrag)) {
|
|
3286
|
+
return false;
|
|
3287
|
+
}
|
|
3288
|
+
const currentCenter = event.offsetCenter;
|
|
3289
|
+
if (!this.isPointInBounds(this.getCenter(event), event)) {
|
|
3290
|
+
return false;
|
|
3291
|
+
}
|
|
3292
|
+
const isTrackpad = event.pointerType === "trackpad";
|
|
3293
|
+
const startCenter = {
|
|
3294
|
+
x: currentCenter.x - (isTrackpad ? 0 : event.deltaX),
|
|
3295
|
+
y: currentCenter.y - (isTrackpad ? 0 : event.deltaY)
|
|
3296
|
+
};
|
|
3297
|
+
const startEvent = { ...event, offsetCenter: startCenter };
|
|
3298
|
+
const pos = this.getCenter(startEvent);
|
|
3299
|
+
const newControllerState = multiTouchDrag === "pan" ? this.controllerState.panStart({ pos }, this._getConstraintContext("pan", "start")) : this.controllerState.rotateStart({ pos }, this._getConstraintContext("rotate", "start"));
|
|
3300
|
+
this._multiPanMode = multiTouchDrag;
|
|
3301
|
+
this._multiPanStartCenter = startCenter;
|
|
3302
|
+
this.updateViewport(newControllerState, NO_TRANSITION_PROPS, { isDragging: true });
|
|
3303
|
+
return true;
|
|
3304
|
+
}
|
|
3305
|
+
_onMultiPan(event) {
|
|
3306
|
+
const { mode, event: panEvent } = this._getMultiPanEvent(event);
|
|
3307
|
+
if (!mode || !panEvent || !this.isDragging()) {
|
|
3308
|
+
return false;
|
|
3309
|
+
}
|
|
3310
|
+
return mode === "pan" ? this._onPanMove(panEvent) : this._onPanRotate(panEvent);
|
|
3311
|
+
}
|
|
3312
|
+
_onMultiPanEnd(event) {
|
|
3313
|
+
const { mode, event: panEvent } = this._getMultiPanEvent(event);
|
|
3314
|
+
if (!mode || !panEvent || !this.isDragging()) {
|
|
3315
|
+
this._resetMultiPan();
|
|
3316
|
+
return false;
|
|
3317
|
+
}
|
|
3318
|
+
const handled = mode === "pan" ? this._onPanMoveEnd(panEvent) : this._onPanRotateEnd(panEvent);
|
|
3319
|
+
this._resetMultiPan();
|
|
3320
|
+
return handled;
|
|
3321
|
+
}
|
|
3322
|
+
_isTrackpadGestureAllowed(event) {
|
|
3323
|
+
return event.pointerType !== "trackpad" || this.trackpadGesture;
|
|
3324
|
+
}
|
|
3325
|
+
_isMultiPanEventAllowed(event, mode) {
|
|
3326
|
+
if (event.pointerType === "trackpad") {
|
|
3327
|
+
return this.trackpadGesture && (mode === "pan" ? this.dragPan : this.dragRotate);
|
|
3328
|
+
}
|
|
3329
|
+
return event.pointerType === "touch" && (mode === "pan" ? this.dragPan : this.dragRotate);
|
|
3330
|
+
}
|
|
3331
|
+
_getMultiPanEvent(event) {
|
|
3332
|
+
const mode = this._multiPanMode;
|
|
3333
|
+
const startCenter = this._multiPanStartCenter;
|
|
3334
|
+
if (!mode || !startCenter) {
|
|
3335
|
+
return { mode: null, event: null };
|
|
3336
|
+
}
|
|
3337
|
+
return {
|
|
3338
|
+
mode,
|
|
3339
|
+
event: {
|
|
3340
|
+
...event,
|
|
3341
|
+
offsetCenter: {
|
|
3342
|
+
x: startCenter.x + event.deltaX,
|
|
3343
|
+
y: startCenter.y + event.deltaY
|
|
3344
|
+
}
|
|
3345
|
+
}
|
|
3346
|
+
};
|
|
3347
|
+
}
|
|
3348
|
+
_resetMultiPan() {
|
|
3349
|
+
this._multiPanMode = null;
|
|
3350
|
+
this._multiPanStartCenter = null;
|
|
3351
|
+
}
|
|
3352
|
+
// Default handler for the `pinchstart` event.
|
|
3353
|
+
_onPinchStart(event) {
|
|
3354
|
+
this._doubleClickDragAnchor = null;
|
|
3355
|
+
const pos = this.getCenter(event);
|
|
3356
|
+
if (!this.isPointInBounds(pos, event)) {
|
|
3357
|
+
return false;
|
|
3358
|
+
}
|
|
3359
|
+
const newControllerState = this.controllerState.zoomStart({ pos }, this._getConstraintContext("zoom", "start")).rotateStart({ pos }, this._getConstraintContext("rotate", "start"));
|
|
3360
|
+
pinchEventWorkaround._startPinchRotation = event.rotation;
|
|
3361
|
+
pinchEventWorkaround._lastPinchEvent = event;
|
|
3362
|
+
this.updateViewport(newControllerState, NO_TRANSITION_PROPS, { isDragging: true });
|
|
3363
|
+
return true;
|
|
3364
|
+
}
|
|
3365
|
+
// Default handler for the `pinchmove` and `pinchend` events.
|
|
3366
|
+
_onPinch(event) {
|
|
3367
|
+
if (!this.touchZoom && !this.touchRotate) {
|
|
3368
|
+
return false;
|
|
3369
|
+
}
|
|
3370
|
+
if (!this.isDragging()) {
|
|
3371
|
+
return false;
|
|
3372
|
+
}
|
|
3373
|
+
let newControllerState = this.controllerState;
|
|
3374
|
+
if (this.touchZoom) {
|
|
3375
|
+
const { scale } = event;
|
|
3376
|
+
const pos = this.getCenter(event);
|
|
3377
|
+
newControllerState = newControllerState.zoom(
|
|
3378
|
+
{ pos, scale },
|
|
3379
|
+
this._getConstraintContext("zoom", "update")
|
|
3380
|
+
);
|
|
3381
|
+
}
|
|
3382
|
+
if (this.touchRotate) {
|
|
3383
|
+
const { rotation } = event;
|
|
3384
|
+
newControllerState = newControllerState.rotate(
|
|
3385
|
+
{ deltaAngleX: pinchEventWorkaround._startPinchRotation - rotation },
|
|
3386
|
+
this._getConstraintContext("rotate", "update")
|
|
3387
|
+
);
|
|
3388
|
+
}
|
|
3389
|
+
this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
|
|
3390
|
+
isDragging: true,
|
|
3391
|
+
isPanning: this.touchZoom,
|
|
3392
|
+
isZooming: this.touchZoom,
|
|
3393
|
+
isRotating: this.touchRotate
|
|
3394
|
+
});
|
|
3395
|
+
pinchEventWorkaround._lastPinchEvent = event;
|
|
3396
|
+
return true;
|
|
3397
|
+
}
|
|
3398
|
+
_onPinchEnd(event) {
|
|
3399
|
+
if (!this.isDragging()) {
|
|
3400
|
+
return false;
|
|
3401
|
+
}
|
|
3402
|
+
const { inertia } = this;
|
|
3403
|
+
const { _lastPinchEvent } = pinchEventWorkaround;
|
|
3404
|
+
if (this.touchZoom && inertia && _lastPinchEvent && event.scale !== _lastPinchEvent.scale) {
|
|
3405
|
+
const pos = this.getCenter(event);
|
|
3406
|
+
let newControllerState = this.controllerState.rotateEnd();
|
|
3407
|
+
const z = Math.log2(event.scale);
|
|
3408
|
+
const velocityZ = (z - Math.log2(_lastPinchEvent.scale)) / (event.deltaTime - _lastPinchEvent.deltaTime);
|
|
3409
|
+
const endScale = Math.pow(2, z + velocityZ * inertia / 2);
|
|
3410
|
+
newControllerState = newControllerState.zoom({ pos, scale: endScale }).zoomEnd();
|
|
3411
|
+
this.updateViewport(
|
|
3412
|
+
newControllerState,
|
|
3413
|
+
{
|
|
3414
|
+
...this._getTransitionProps({ around: pos }),
|
|
3415
|
+
transitionDuration: inertia,
|
|
3416
|
+
transitionEasing: INERTIA_EASING
|
|
3417
|
+
},
|
|
3418
|
+
{
|
|
3419
|
+
isDragging: false,
|
|
3420
|
+
isPanning: this.touchZoom,
|
|
3421
|
+
isZooming: this.touchZoom,
|
|
3422
|
+
isRotating: false
|
|
3423
|
+
}
|
|
3424
|
+
);
|
|
3425
|
+
this.blockEvents(inertia);
|
|
3426
|
+
} else {
|
|
3427
|
+
const currentControllerState = this.controllerState;
|
|
3428
|
+
const zoomConstraintContext = this._getConstraintContext("zoom", "end");
|
|
3429
|
+
const rotateConstraintContext = this._getConstraintContext("rotate", "end");
|
|
3430
|
+
const newControllerState = currentControllerState.zoomEnd(zoomConstraintContext).rotateEnd(rotateConstraintContext);
|
|
3431
|
+
const reboundTransition = this._getReboundTransition(
|
|
3432
|
+
this.touchZoom ? zoomConstraintContext : rotateConstraintContext,
|
|
3433
|
+
newControllerState
|
|
3434
|
+
);
|
|
3435
|
+
this.updateViewport(newControllerState, reboundTransition, {
|
|
3436
|
+
isDragging: false,
|
|
3437
|
+
isPanning: Boolean(reboundTransition) && this.touchZoom,
|
|
3438
|
+
isZooming: Boolean(reboundTransition) && this.touchZoom,
|
|
3439
|
+
isRotating: Boolean(reboundTransition) && this.touchRotate
|
|
3440
|
+
});
|
|
3441
|
+
}
|
|
3442
|
+
pinchEventWorkaround._startPinchRotation = null;
|
|
3443
|
+
pinchEventWorkaround._lastPinchEvent = null;
|
|
3444
|
+
return true;
|
|
3445
|
+
}
|
|
3446
|
+
// Default handler for the `dblclick` event.
|
|
3447
|
+
_onDoubleClick(event) {
|
|
3448
|
+
if (!this.doubleClickZoom) {
|
|
3449
|
+
return false;
|
|
3450
|
+
}
|
|
3451
|
+
if (Date.now() < this._suppressDoubleClickUntil) {
|
|
3452
|
+
return false;
|
|
3453
|
+
}
|
|
3454
|
+
const pos = this.getCenter(event);
|
|
3455
|
+
if (!this.isPointInBounds(pos, event)) {
|
|
3456
|
+
return false;
|
|
3457
|
+
}
|
|
3458
|
+
const isZoomOut = this.isFunctionKeyPressed(event);
|
|
3459
|
+
const newControllerState = this.controllerState.zoom({ pos, scale: isZoomOut ? 0.5 : 2 });
|
|
3460
|
+
this.updateViewport(newControllerState, this._getTransitionProps({ around: pos }), {
|
|
3461
|
+
isZooming: true,
|
|
3462
|
+
isPanning: true
|
|
3463
|
+
});
|
|
3464
|
+
this.blockEvents(100);
|
|
3465
|
+
return true;
|
|
3466
|
+
}
|
|
3467
|
+
_onDoubleClickDragStart(event) {
|
|
3468
|
+
if (!this.doubleClickDragZoom) {
|
|
3469
|
+
this._doubleClickDragAnchor = null;
|
|
3470
|
+
return false;
|
|
3471
|
+
}
|
|
3472
|
+
const pos = this.getCenter(event);
|
|
3473
|
+
if (!this.isPointInBounds(pos, event)) {
|
|
3474
|
+
this._doubleClickDragAnchor = null;
|
|
3475
|
+
return false;
|
|
3476
|
+
}
|
|
3477
|
+
this._doubleClickDragAnchor = pos;
|
|
3478
|
+
let newControllerState = this.controllerState.zoomStart(
|
|
3479
|
+
{ pos },
|
|
3480
|
+
this._getConstraintContext("zoom", "start")
|
|
3481
|
+
);
|
|
3482
|
+
if (event.scale !== 1) {
|
|
3483
|
+
newControllerState = newControllerState.zoom(
|
|
3484
|
+
{ pos, scale: event.scale },
|
|
3485
|
+
this._getConstraintContext("zoom", "update")
|
|
3486
|
+
);
|
|
3487
|
+
}
|
|
3488
|
+
this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
|
|
3489
|
+
isDragging: true,
|
|
3490
|
+
isPanning: true,
|
|
3491
|
+
isZooming: true
|
|
3492
|
+
});
|
|
3493
|
+
return true;
|
|
3494
|
+
}
|
|
3495
|
+
_onDoubleClickDrag(event) {
|
|
3496
|
+
const pos = this._doubleClickDragAnchor;
|
|
3497
|
+
if (!pos) {
|
|
3498
|
+
return false;
|
|
3499
|
+
}
|
|
3500
|
+
const newControllerState = this.controllerState.zoom(
|
|
3501
|
+
{ pos, scale: event.scale },
|
|
3502
|
+
this._getConstraintContext("zoom", "update")
|
|
3503
|
+
);
|
|
3504
|
+
this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
|
|
3505
|
+
isDragging: true,
|
|
3506
|
+
isPanning: true,
|
|
3507
|
+
isZooming: true
|
|
3508
|
+
});
|
|
3509
|
+
return true;
|
|
3510
|
+
}
|
|
3511
|
+
_onDoubleClickDragEnd(_event) {
|
|
3512
|
+
const pos = this._doubleClickDragAnchor;
|
|
3513
|
+
if (!pos) {
|
|
3514
|
+
return false;
|
|
3515
|
+
}
|
|
3516
|
+
this._doubleClickDragAnchor = null;
|
|
3517
|
+
const currentControllerState = this.controllerState;
|
|
3518
|
+
const constraintContext = this._getConstraintContext("zoom", "end");
|
|
3519
|
+
const newControllerState = currentControllerState.zoomEnd(constraintContext);
|
|
3520
|
+
const reboundTransition = this._getReboundTransition(constraintContext, newControllerState);
|
|
3521
|
+
this.updateViewport(newControllerState, reboundTransition, {
|
|
3522
|
+
isDragging: false,
|
|
3523
|
+
isPanning: Boolean(reboundTransition),
|
|
3524
|
+
isZooming: Boolean(reboundTransition)
|
|
3525
|
+
});
|
|
3526
|
+
this._suppressDoubleClickUntil = Date.now() + 100;
|
|
3527
|
+
this.blockEvents(100);
|
|
3528
|
+
return true;
|
|
3529
|
+
}
|
|
3530
|
+
// Default handler for the `keydown` event
|
|
3531
|
+
_onKeyDown(event) {
|
|
3532
|
+
if (!this.keyboard) {
|
|
3533
|
+
return false;
|
|
3534
|
+
}
|
|
3535
|
+
const funcKey = this.isFunctionKeyPressed(event);
|
|
3536
|
+
const { zoomSpeed, moveSpeed, rotateSpeedX, rotateSpeedY } = this.keyboard === true ? {} : this.keyboard;
|
|
3537
|
+
const { controllerState } = this;
|
|
3538
|
+
let newControllerState;
|
|
3539
|
+
const interactionState = {};
|
|
3540
|
+
switch (event.srcEvent.code) {
|
|
3541
|
+
case "Minus":
|
|
3542
|
+
newControllerState = funcKey ? controllerState.zoomOut(zoomSpeed).zoomOut(zoomSpeed) : controllerState.zoomOut(zoomSpeed);
|
|
3543
|
+
interactionState.isZooming = true;
|
|
3544
|
+
break;
|
|
3545
|
+
case "Equal":
|
|
3546
|
+
newControllerState = funcKey ? controllerState.zoomIn(zoomSpeed).zoomIn(zoomSpeed) : controllerState.zoomIn(zoomSpeed);
|
|
3547
|
+
interactionState.isZooming = true;
|
|
3548
|
+
break;
|
|
3549
|
+
case "ArrowLeft":
|
|
3550
|
+
if (funcKey) {
|
|
3551
|
+
newControllerState = controllerState.rotateLeft(rotateSpeedX);
|
|
3552
|
+
interactionState.isRotating = true;
|
|
3553
|
+
} else {
|
|
3554
|
+
newControllerState = controllerState.moveLeft(moveSpeed);
|
|
3555
|
+
interactionState.isPanning = true;
|
|
3556
|
+
}
|
|
3557
|
+
break;
|
|
3558
|
+
case "ArrowRight":
|
|
3559
|
+
if (funcKey) {
|
|
3560
|
+
newControllerState = controllerState.rotateRight(rotateSpeedX);
|
|
3561
|
+
interactionState.isRotating = true;
|
|
3562
|
+
} else {
|
|
3563
|
+
newControllerState = controllerState.moveRight(moveSpeed);
|
|
3564
|
+
interactionState.isPanning = true;
|
|
3565
|
+
}
|
|
3566
|
+
break;
|
|
3567
|
+
case "ArrowUp":
|
|
3568
|
+
if (funcKey) {
|
|
3569
|
+
newControllerState = controllerState.rotateUp(rotateSpeedY);
|
|
3570
|
+
interactionState.isRotating = true;
|
|
3571
|
+
} else {
|
|
3572
|
+
newControllerState = controllerState.moveUp(moveSpeed);
|
|
3573
|
+
interactionState.isPanning = true;
|
|
3574
|
+
}
|
|
3575
|
+
break;
|
|
3576
|
+
case "ArrowDown":
|
|
3577
|
+
if (funcKey) {
|
|
3578
|
+
newControllerState = controllerState.rotateDown(rotateSpeedY);
|
|
3579
|
+
interactionState.isRotating = true;
|
|
3580
|
+
} else {
|
|
3581
|
+
newControllerState = controllerState.moveDown(moveSpeed);
|
|
3582
|
+
interactionState.isPanning = true;
|
|
3583
|
+
}
|
|
3584
|
+
break;
|
|
3585
|
+
default:
|
|
3586
|
+
return false;
|
|
3587
|
+
}
|
|
3588
|
+
this.updateViewport(newControllerState, this._getTransitionProps(), interactionState);
|
|
3589
|
+
return true;
|
|
3590
|
+
}
|
|
3591
|
+
_getTransitionProps(opts) {
|
|
3592
|
+
const { transition } = this;
|
|
3593
|
+
if (!transition || !transition.transitionInterpolator) {
|
|
3594
|
+
return NO_TRANSITION_PROPS;
|
|
3595
|
+
}
|
|
3596
|
+
return opts ? {
|
|
3597
|
+
...transition,
|
|
3598
|
+
transitionInterpolator: new LinearInterpolator({
|
|
3599
|
+
...opts,
|
|
3600
|
+
...transition.transitionInterpolator.opts,
|
|
3601
|
+
makeViewport: this.controllerState.makeViewport
|
|
3602
|
+
})
|
|
3603
|
+
} : transition;
|
|
3604
|
+
}
|
|
3605
|
+
};
|
|
3606
|
+
|
|
3607
|
+
// ../core/src/controllers/view-state.ts
|
|
3608
|
+
var CONSTRAINT_AROUND = /* @__PURE__ */ Symbol("constraintAround");
|
|
3609
|
+
var ViewState = class {
|
|
3610
|
+
constructor(props, state, makeViewport, constraintContext) {
|
|
3611
|
+
this.makeViewport = makeViewport;
|
|
3612
|
+
this._viewportProps = this.applyConstraints(props, constraintContext);
|
|
3613
|
+
this._state = state;
|
|
3614
|
+
}
|
|
3615
|
+
getViewportProps() {
|
|
3616
|
+
return this._viewportProps;
|
|
3617
|
+
}
|
|
3618
|
+
getState() {
|
|
3619
|
+
return this._state;
|
|
3620
|
+
}
|
|
3621
|
+
};
|
|
3622
|
+
|
|
3623
|
+
// ../core/src/controllers/utils.ts
|
|
3624
|
+
function applyRubberBand(value, constrainedValue, range) {
|
|
3625
|
+
const overshoot = value - constrainedValue;
|
|
3626
|
+
return overshoot && Number.isFinite(overshoot) ? constrainedValue + overshoot * range / (range + Math.abs(overshoot)) : constrainedValue;
|
|
3627
|
+
}
|
|
3628
|
+
function getMaxBoundsRect(width, height, padding) {
|
|
3629
|
+
const left = getPosition(parsePosition((padding == null ? void 0 : padding.left) ?? 0), width);
|
|
3630
|
+
const right = getPosition(parsePosition((padding == null ? void 0 : padding.right) ?? 0), width);
|
|
3631
|
+
const top = getPosition(parsePosition((padding == null ? void 0 : padding.top) ?? 0), height);
|
|
3632
|
+
const bottom = getPosition(parsePosition((padding == null ? void 0 : padding.bottom) ?? 0), height);
|
|
3633
|
+
return {
|
|
3634
|
+
x: left,
|
|
3635
|
+
y: top,
|
|
3636
|
+
width: width - left - right,
|
|
3637
|
+
height: height - top - bottom
|
|
3638
|
+
};
|
|
3639
|
+
}
|
|
3640
|
+
function getMaxBoundsExtents(viewport, target, rect) {
|
|
3641
|
+
let [x, y] = viewport.project(target);
|
|
3642
|
+
x = Number.isFinite(x) ? x : viewport.width / 2;
|
|
3643
|
+
y = Number.isFinite(y) ? y : viewport.height / 2;
|
|
3644
|
+
return {
|
|
3645
|
+
left: x - rect.x,
|
|
3646
|
+
right: rect.x + rect.width - x,
|
|
3647
|
+
top: y - rect.y,
|
|
3648
|
+
bottom: rect.y + rect.height - y
|
|
3649
|
+
};
|
|
3650
|
+
}
|
|
3651
|
+
|
|
3652
|
+
// ../core/src/controllers/map-controller.ts
|
|
3653
|
+
var import_web_mercator5 = require("@math.gl/web-mercator");
|
|
3654
|
+
var PITCH_MOUSE_THRESHOLD = 5;
|
|
3655
|
+
var PITCH_ACCEL = 1.2;
|
|
3656
|
+
var WEB_MERCATOR_TILE_SIZE = 512;
|
|
3657
|
+
var WEB_MERCATOR_MAX_BOUNDS = [
|
|
3658
|
+
[-Infinity, -90],
|
|
3659
|
+
[Infinity, 90]
|
|
3660
|
+
];
|
|
3661
|
+
var ZOOM_RUBBER_BAND_RANGE = 1;
|
|
3662
|
+
function lngLatToWorld2([lng, lat]) {
|
|
3663
|
+
if (Math.abs(lat) > 90) {
|
|
3664
|
+
lat = Math.sign(lat) * 90;
|
|
3665
|
+
}
|
|
3666
|
+
if (Number.isFinite(lng)) {
|
|
3667
|
+
const [x, y2] = (0, import_web_mercator5.lngLatToWorld)([lng, lat]);
|
|
3668
|
+
return [x, (0, import_core9.clamp)(y2, 0, WEB_MERCATOR_TILE_SIZE)];
|
|
3669
|
+
}
|
|
3670
|
+
const [, y] = (0, import_web_mercator5.lngLatToWorld)([0, lat]);
|
|
3671
|
+
return [lng, (0, import_core9.clamp)(y, 0, WEB_MERCATOR_TILE_SIZE)];
|
|
3672
|
+
}
|
|
3673
|
+
var MapState = class extends ViewState {
|
|
3674
|
+
constructor(options) {
|
|
3675
|
+
const {
|
|
3676
|
+
/** Mapbox viewport properties */
|
|
3677
|
+
/** The width of the viewport */
|
|
3678
|
+
width,
|
|
3679
|
+
/** The height of the viewport */
|
|
3680
|
+
height,
|
|
3681
|
+
/** The latitude at the center of the viewport */
|
|
3682
|
+
latitude,
|
|
3683
|
+
/** The longitude at the center of the viewport */
|
|
3684
|
+
longitude,
|
|
3685
|
+
/** The tile zoom level of the map. */
|
|
3686
|
+
zoom,
|
|
3687
|
+
/** The bearing of the viewport in degrees */
|
|
3688
|
+
bearing = 0,
|
|
3689
|
+
/** The pitch of the viewport in degrees */
|
|
3690
|
+
pitch = 0,
|
|
3691
|
+
/**
|
|
3692
|
+
* Specify the altitude of the viewport camera
|
|
3693
|
+
* Unit: map heights, default 1.5
|
|
3694
|
+
* Non-public API, see https://github.com/mapbox/mapbox-gl-js/issues/1137
|
|
3695
|
+
*/
|
|
3696
|
+
altitude = 1.5,
|
|
3697
|
+
/** Viewport position */
|
|
3698
|
+
position = [0, 0, 0],
|
|
3699
|
+
/** Viewport constraints */
|
|
3700
|
+
maxZoom = 20,
|
|
3701
|
+
minZoom = 0,
|
|
3702
|
+
maxPitch = 60,
|
|
3703
|
+
minPitch = 0,
|
|
3704
|
+
/** Interaction states, required to calculate change during transform */
|
|
3705
|
+
/* The point on map being grabbed when the operation first started */
|
|
3706
|
+
startPanLngLat,
|
|
3707
|
+
/* Center of the zoom when the operation first started */
|
|
3708
|
+
startZoomLngLat,
|
|
3709
|
+
/* Pointer position when rotation started */
|
|
3710
|
+
startRotatePos,
|
|
3711
|
+
/* The lng/lat point at the rotation pivot (where rotation started) */
|
|
3712
|
+
startRotateLngLat,
|
|
3713
|
+
/** Bearing when current perspective rotate operation started */
|
|
3714
|
+
startBearing,
|
|
3715
|
+
/** Pitch when current perspective rotate operation started */
|
|
3716
|
+
startPitch,
|
|
3717
|
+
/** Zoom when current zoom operation started */
|
|
3718
|
+
startZoom,
|
|
3719
|
+
/** Normalize viewport props to fit map height into viewport */
|
|
3720
|
+
normalize = true,
|
|
3721
|
+
rubberBand = false
|
|
3722
|
+
} = options;
|
|
3723
|
+
const { [CONSTRAINT_AROUND]: constraintAround } = options;
|
|
3724
|
+
assert(Number.isFinite(longitude));
|
|
3725
|
+
assert(Number.isFinite(latitude));
|
|
3726
|
+
assert(Number.isFinite(zoom));
|
|
3727
|
+
const maxBounds = options.maxBounds || (normalize ? WEB_MERCATOR_MAX_BOUNDS : null);
|
|
3728
|
+
const maxBoundsPadding = options.maxBoundsPadding || null;
|
|
3729
|
+
super(
|
|
3730
|
+
{
|
|
3731
|
+
width,
|
|
3732
|
+
height,
|
|
3733
|
+
latitude,
|
|
3734
|
+
longitude,
|
|
3735
|
+
zoom,
|
|
3736
|
+
bearing,
|
|
3737
|
+
pitch,
|
|
3738
|
+
altitude,
|
|
3739
|
+
maxZoom,
|
|
3740
|
+
minZoom,
|
|
3741
|
+
maxPitch,
|
|
3742
|
+
minPitch,
|
|
3743
|
+
normalize,
|
|
3744
|
+
position,
|
|
3745
|
+
maxBounds,
|
|
3746
|
+
maxBoundsPadding,
|
|
3747
|
+
rubberBand,
|
|
3748
|
+
...{ [CONSTRAINT_AROUND]: constraintAround }
|
|
3749
|
+
},
|
|
3750
|
+
{
|
|
3751
|
+
startPanLngLat,
|
|
3752
|
+
startZoomLngLat,
|
|
3753
|
+
startRotatePos,
|
|
3754
|
+
startRotateLngLat,
|
|
3755
|
+
startBearing,
|
|
3756
|
+
startPitch,
|
|
3757
|
+
startZoom
|
|
3758
|
+
},
|
|
3759
|
+
options.makeViewport,
|
|
3760
|
+
options.constraintContext
|
|
3761
|
+
);
|
|
3762
|
+
this.getAltitude = options.getAltitude;
|
|
3763
|
+
}
|
|
3764
|
+
/**
|
|
3765
|
+
* Start panning
|
|
3766
|
+
* @param {[Number, Number]} pos - position on screen where the pointer grabs
|
|
3767
|
+
*/
|
|
3768
|
+
panStart({ pos }, constraintContext) {
|
|
3769
|
+
return this._getUpdatedState(
|
|
3770
|
+
{
|
|
3771
|
+
startPanLngLat: this._unproject(pos)
|
|
3772
|
+
},
|
|
3773
|
+
constraintContext
|
|
3774
|
+
);
|
|
3775
|
+
}
|
|
3776
|
+
/**
|
|
3777
|
+
* Pan
|
|
3778
|
+
* @param {[Number, Number]} pos - position on screen where the pointer is
|
|
3779
|
+
* @param {[Number, Number], optional} startPos - where the pointer grabbed at
|
|
3780
|
+
* the start of the operation. Must be supplied of `panStart()` was not called
|
|
3781
|
+
*/
|
|
3782
|
+
pan({ pos, startPos }, constraintContext) {
|
|
3783
|
+
const startPanLngLat = this.getState().startPanLngLat || this._unproject(startPos);
|
|
3784
|
+
if (!startPanLngLat) {
|
|
3785
|
+
return this;
|
|
3786
|
+
}
|
|
3787
|
+
const viewport = this.makeViewport(this.getViewportProps());
|
|
3788
|
+
const newProps = viewport.panByPosition(startPanLngLat, pos);
|
|
3789
|
+
return this._getUpdatedState(newProps, constraintContext);
|
|
3790
|
+
}
|
|
3791
|
+
/**
|
|
3792
|
+
* End panning
|
|
3793
|
+
* Must call if `panStart()` was called
|
|
3794
|
+
*/
|
|
3795
|
+
panEnd(constraintContext) {
|
|
3796
|
+
return this._getUpdatedState(
|
|
3797
|
+
{
|
|
3798
|
+
startPanLngLat: null
|
|
3799
|
+
},
|
|
3800
|
+
constraintContext
|
|
3801
|
+
);
|
|
3802
|
+
}
|
|
3803
|
+
/**
|
|
3804
|
+
* Start rotating
|
|
3805
|
+
* @param {[Number, Number]} pos - position on screen where the center is
|
|
3806
|
+
*/
|
|
3807
|
+
rotateStart({ pos }) {
|
|
3808
|
+
var _a2;
|
|
3809
|
+
const altitude = (_a2 = this.getAltitude) == null ? void 0 : _a2.call(this, pos);
|
|
3810
|
+
return this._getUpdatedState({
|
|
3811
|
+
startRotatePos: pos,
|
|
3812
|
+
startRotateLngLat: altitude !== void 0 ? this._unproject3D(pos, altitude) : void 0,
|
|
3813
|
+
startBearing: this.getViewportProps().bearing,
|
|
3814
|
+
startPitch: this.getViewportProps().pitch
|
|
3815
|
+
});
|
|
3816
|
+
}
|
|
3817
|
+
/**
|
|
3818
|
+
* Rotate
|
|
3819
|
+
* @param {[Number, Number]} pos - position on screen where the center is
|
|
3820
|
+
*/
|
|
3821
|
+
rotate({
|
|
3822
|
+
pos,
|
|
3823
|
+
deltaAngleX = 0,
|
|
3824
|
+
deltaAngleY = 0
|
|
3825
|
+
}) {
|
|
3826
|
+
const { startRotatePos, startRotateLngLat, startBearing, startPitch } = this.getState();
|
|
3827
|
+
if (!startRotatePos || startBearing === void 0 || startPitch === void 0) {
|
|
3828
|
+
return this;
|
|
3829
|
+
}
|
|
3830
|
+
let newRotation;
|
|
3831
|
+
if (pos) {
|
|
3832
|
+
newRotation = this._getNewRotation(pos, startRotatePos, startPitch, startBearing);
|
|
3833
|
+
} else {
|
|
3834
|
+
newRotation = {
|
|
3835
|
+
bearing: startBearing + deltaAngleX,
|
|
3836
|
+
pitch: startPitch + deltaAngleY
|
|
3837
|
+
};
|
|
3838
|
+
}
|
|
3839
|
+
if (startRotateLngLat) {
|
|
3840
|
+
const rotatedViewport = this.makeViewport({
|
|
3841
|
+
...this.getViewportProps(),
|
|
3842
|
+
...newRotation
|
|
3843
|
+
});
|
|
3844
|
+
const panMethod = "panByPosition3D" in rotatedViewport ? "panByPosition3D" : "panByPosition";
|
|
3845
|
+
return this._getUpdatedState({
|
|
3846
|
+
...newRotation,
|
|
3847
|
+
...rotatedViewport[panMethod](startRotateLngLat, startRotatePos)
|
|
3848
|
+
});
|
|
3849
|
+
}
|
|
3850
|
+
return this._getUpdatedState(newRotation);
|
|
3851
|
+
}
|
|
3852
|
+
/**
|
|
3853
|
+
* End rotating
|
|
3854
|
+
* Must call if `rotateStart()` was called
|
|
3855
|
+
*/
|
|
3856
|
+
rotateEnd() {
|
|
3857
|
+
return this._getUpdatedState({
|
|
3858
|
+
startRotatePos: null,
|
|
3859
|
+
startRotateLngLat: null,
|
|
3860
|
+
startBearing: null,
|
|
3861
|
+
startPitch: null
|
|
3862
|
+
});
|
|
3863
|
+
}
|
|
3864
|
+
/**
|
|
3865
|
+
* Start zooming
|
|
3866
|
+
* @param {[Number, Number]} pos - position on screen where the center is
|
|
3867
|
+
*/
|
|
3868
|
+
zoomStart({ pos }, constraintContext) {
|
|
3869
|
+
return this._getUpdatedState(
|
|
3870
|
+
{
|
|
3871
|
+
startZoomLngLat: this._unproject(pos),
|
|
3872
|
+
startZoom: this.getViewportProps().zoom
|
|
3873
|
+
},
|
|
3874
|
+
constraintContext
|
|
3875
|
+
);
|
|
3876
|
+
}
|
|
3877
|
+
/**
|
|
3878
|
+
* Zoom
|
|
3879
|
+
* @param {[Number, Number]} pos - position on screen where the current center is
|
|
3880
|
+
* @param {[Number, Number]} startPos - the center position at
|
|
3881
|
+
* the start of the operation. Must be supplied of `zoomStart()` was not called
|
|
3882
|
+
* @param {Number} scale - a number between [0, 1] specifying the accumulated
|
|
3883
|
+
* relative scale.
|
|
3884
|
+
*/
|
|
3885
|
+
zoom({
|
|
3886
|
+
pos,
|
|
3887
|
+
startPos,
|
|
3888
|
+
scale
|
|
3889
|
+
}, constraintContext) {
|
|
3890
|
+
let { startZoom, startZoomLngLat } = this.getState();
|
|
3891
|
+
if (!startZoomLngLat) {
|
|
3892
|
+
startZoom = this.getViewportProps().zoom;
|
|
3893
|
+
startZoomLngLat = this._unproject(startPos) || this._unproject(pos);
|
|
3894
|
+
}
|
|
3895
|
+
if (!startZoomLngLat) {
|
|
3896
|
+
return this;
|
|
3897
|
+
}
|
|
3898
|
+
return this._getUpdatedState(
|
|
3899
|
+
{
|
|
3900
|
+
zoom: startZoom + Math.log2(scale),
|
|
3901
|
+
[CONSTRAINT_AROUND]: { position: startZoomLngLat, screenPosition: pos }
|
|
3902
|
+
},
|
|
3903
|
+
constraintContext
|
|
3904
|
+
);
|
|
3905
|
+
}
|
|
3906
|
+
/**
|
|
3907
|
+
* End zooming
|
|
3908
|
+
* Must call if `zoomStart()` was called
|
|
3909
|
+
*/
|
|
3910
|
+
zoomEnd(constraintContext) {
|
|
3911
|
+
return this._getUpdatedState(
|
|
3912
|
+
{
|
|
3913
|
+
startZoomLngLat: null,
|
|
3914
|
+
startZoom: null
|
|
3915
|
+
},
|
|
3916
|
+
constraintContext
|
|
3917
|
+
);
|
|
3918
|
+
}
|
|
3919
|
+
zoomIn(speed = 2, constraintContext) {
|
|
3920
|
+
return this._zoomFromCenter(speed, constraintContext);
|
|
3921
|
+
}
|
|
3922
|
+
zoomOut(speed = 2, constraintContext) {
|
|
3923
|
+
return this._zoomFromCenter(1 / speed, constraintContext);
|
|
3924
|
+
}
|
|
3925
|
+
moveLeft(speed = 100, constraintContext) {
|
|
3926
|
+
return this._panFromCenter([speed, 0], constraintContext);
|
|
3927
|
+
}
|
|
3928
|
+
moveRight(speed = 100, constraintContext) {
|
|
3929
|
+
return this._panFromCenter([-speed, 0], constraintContext);
|
|
3930
|
+
}
|
|
3931
|
+
moveUp(speed = 100, constraintContext) {
|
|
3932
|
+
return this._panFromCenter([0, speed], constraintContext);
|
|
3933
|
+
}
|
|
3934
|
+
moveDown(speed = 100, constraintContext) {
|
|
3935
|
+
return this._panFromCenter([0, -speed], constraintContext);
|
|
3936
|
+
}
|
|
3937
|
+
rotateLeft(speed = 15) {
|
|
3938
|
+
return this._getUpdatedState({
|
|
3939
|
+
bearing: this.getViewportProps().bearing - speed
|
|
3940
|
+
});
|
|
3941
|
+
}
|
|
3942
|
+
rotateRight(speed = 15) {
|
|
3943
|
+
return this._getUpdatedState({
|
|
3944
|
+
bearing: this.getViewportProps().bearing + speed
|
|
3945
|
+
});
|
|
3946
|
+
}
|
|
3947
|
+
rotateUp(speed = 10) {
|
|
3948
|
+
return this._getUpdatedState({
|
|
3949
|
+
pitch: this.getViewportProps().pitch + speed
|
|
3950
|
+
});
|
|
3951
|
+
}
|
|
3952
|
+
rotateDown(speed = 10) {
|
|
3953
|
+
return this._getUpdatedState({
|
|
3954
|
+
pitch: this.getViewportProps().pitch - speed
|
|
3955
|
+
});
|
|
3956
|
+
}
|
|
3957
|
+
shortestPathFrom(viewState) {
|
|
3958
|
+
const fromProps = viewState.getViewportProps();
|
|
3959
|
+
const props = { ...this.getViewportProps() };
|
|
3960
|
+
const { bearing, longitude } = props;
|
|
3961
|
+
if (Math.abs(bearing - fromProps.bearing) > 180) {
|
|
3962
|
+
props.bearing = bearing < 0 ? bearing + 360 : bearing - 360;
|
|
3963
|
+
}
|
|
3964
|
+
if (Math.abs(longitude - fromProps.longitude) > 180) {
|
|
3965
|
+
props.longitude = longitude < 0 ? longitude + 360 : longitude - 360;
|
|
3966
|
+
}
|
|
3967
|
+
return props;
|
|
3968
|
+
}
|
|
3969
|
+
// Apply any constraints (mathematical or defined by _viewportProps) to map state
|
|
3970
|
+
applyConstraints(props, constraintContext) {
|
|
3971
|
+
const internalProps = props;
|
|
3972
|
+
const constraintAround = internalProps[CONSTRAINT_AROUND];
|
|
3973
|
+
delete internalProps[CONSTRAINT_AROUND];
|
|
3974
|
+
const { maxPitch, minPitch, pitch, bearing, normalize, maxBounds, rubberBand } = props;
|
|
3975
|
+
if (normalize) {
|
|
3976
|
+
if (bearing < -180 || bearing > 180) {
|
|
3977
|
+
props.bearing = mod(bearing + 180, 360) - 180;
|
|
3978
|
+
}
|
|
3979
|
+
}
|
|
3980
|
+
props.pitch = (0, import_core9.clamp)(pitch, minPitch, maxPitch);
|
|
3981
|
+
const constrainedZoom = this._constrainZoom(props.zoom, props);
|
|
3982
|
+
const shouldRubberBand = rubberBand && (constraintContext == null ? void 0 : constraintContext.mode) === "elastic";
|
|
3983
|
+
props.zoom = (constraintContext == null ? void 0 : constraintContext.mode) === "preserve" ? props.zoom : shouldRubberBand ? applyRubberBand(props.zoom, constrainedZoom, ZOOM_RUBBER_BAND_RANGE) : constrainedZoom;
|
|
3984
|
+
if (constraintAround) {
|
|
3985
|
+
const viewport = this.makeViewport(props);
|
|
3986
|
+
Object.assign(
|
|
3987
|
+
props,
|
|
3988
|
+
viewport.panByPosition(constraintAround.position, constraintAround.screenPosition)
|
|
3989
|
+
);
|
|
3990
|
+
}
|
|
3991
|
+
if (normalize && (props.longitude < -180 || props.longitude > 180)) {
|
|
3992
|
+
props.longitude = mod(props.longitude + 180, 360) - 180;
|
|
3993
|
+
}
|
|
3994
|
+
if (maxBounds) {
|
|
3995
|
+
const maxBoundsRect = getMaxBoundsRect(props.width, props.height, props.maxBoundsPadding);
|
|
3996
|
+
const viewport = this.makeViewport({ ...props, bearing: 0, pitch: 0 });
|
|
3997
|
+
const screenExtents = getMaxBoundsExtents(
|
|
3998
|
+
viewport,
|
|
3999
|
+
[props.longitude, props.latitude],
|
|
4000
|
+
maxBoundsRect
|
|
4001
|
+
);
|
|
4002
|
+
const bl = lngLatToWorld2(maxBounds[0]);
|
|
4003
|
+
const tr = lngLatToWorld2(maxBounds[1]);
|
|
4004
|
+
const scale = 2 ** props.zoom;
|
|
4005
|
+
const minimumCenter = [
|
|
4006
|
+
bl[0] + screenExtents.left / scale,
|
|
4007
|
+
bl[1] + screenExtents.bottom / scale
|
|
4008
|
+
];
|
|
4009
|
+
const maximumCenter = [
|
|
4010
|
+
tr[0] - screenExtents.right / scale,
|
|
4011
|
+
tr[1] - screenExtents.top / scale
|
|
4012
|
+
];
|
|
4013
|
+
const center = lngLatToWorld2([props.longitude, props.latitude]);
|
|
4014
|
+
const constrainedCenter = [
|
|
4015
|
+
(0, import_core9.clamp)(center[0], minimumCenter[0], maximumCenter[0]),
|
|
4016
|
+
(0, import_core9.clamp)(center[1], minimumCenter[1], maximumCenter[1])
|
|
4017
|
+
];
|
|
4018
|
+
const displayedCenter = center.slice();
|
|
4019
|
+
if (maxBoundsRect.width >= 0) {
|
|
4020
|
+
displayedCenter[0] = (constraintContext == null ? void 0 : constraintContext.mode) === "preserve" ? center[0] : shouldRubberBand ? applyRubberBand(center[0], constrainedCenter[0], maxBoundsRect.width / 2 / scale) : constrainedCenter[0];
|
|
4021
|
+
}
|
|
4022
|
+
if (maxBoundsRect.height >= 0) {
|
|
4023
|
+
displayedCenter[1] = (constraintContext == null ? void 0 : constraintContext.mode) === "preserve" ? center[1] : shouldRubberBand ? applyRubberBand(center[1], constrainedCenter[1], maxBoundsRect.height / 2 / scale) : constrainedCenter[1];
|
|
4024
|
+
}
|
|
4025
|
+
if (displayedCenter[0] !== center[0] || displayedCenter[1] !== center[1]) {
|
|
4026
|
+
const [displayedLongitude, displayedLatitude] = (0, import_web_mercator5.worldToLngLat)(displayedCenter);
|
|
4027
|
+
if (displayedCenter[0] !== center[0]) {
|
|
4028
|
+
props.longitude = displayedLongitude;
|
|
4029
|
+
}
|
|
4030
|
+
if (displayedCenter[1] !== center[1]) {
|
|
4031
|
+
props.latitude = displayedLatitude;
|
|
4032
|
+
}
|
|
4033
|
+
}
|
|
4034
|
+
}
|
|
4035
|
+
return props;
|
|
4036
|
+
}
|
|
4037
|
+
/* Private methods */
|
|
4038
|
+
_constrainZoom(zoom, props) {
|
|
4039
|
+
props ||= this.getViewportProps();
|
|
4040
|
+
const { maxZoom, maxBounds } = props;
|
|
4041
|
+
const shouldApplyMaxBounds = maxBounds !== null && props.width > 0 && props.height > 0;
|
|
4042
|
+
let { minZoom } = props;
|
|
4043
|
+
if (shouldApplyMaxBounds) {
|
|
4044
|
+
const maxBoundsRect = getMaxBoundsRect(props.width, props.height, props.maxBoundsPadding);
|
|
4045
|
+
const bl = lngLatToWorld2(maxBounds[0]);
|
|
4046
|
+
const tr = lngLatToWorld2(maxBounds[1]);
|
|
4047
|
+
const w = tr[0] - bl[0];
|
|
4048
|
+
const h = tr[1] - bl[1];
|
|
4049
|
+
if (maxBoundsRect.width > 0 && Number.isFinite(w) && w > 0) {
|
|
4050
|
+
minZoom = Math.max(minZoom, Math.log2(maxBoundsRect.width / w));
|
|
4051
|
+
}
|
|
4052
|
+
if (maxBoundsRect.height > 0 && Number.isFinite(h) && h > 0) {
|
|
4053
|
+
minZoom = Math.max(minZoom, Math.log2(maxBoundsRect.height / h));
|
|
4054
|
+
}
|
|
4055
|
+
if (minZoom > maxZoom) minZoom = maxZoom;
|
|
4056
|
+
}
|
|
4057
|
+
return (0, import_core9.clamp)(zoom, minZoom, maxZoom);
|
|
4058
|
+
}
|
|
4059
|
+
_zoomFromCenter(scale, constraintContext) {
|
|
4060
|
+
const { width, height } = this.getViewportProps();
|
|
4061
|
+
return this.zoom(
|
|
4062
|
+
{
|
|
4063
|
+
pos: [width / 2, height / 2],
|
|
4064
|
+
scale
|
|
4065
|
+
},
|
|
4066
|
+
constraintContext
|
|
4067
|
+
);
|
|
4068
|
+
}
|
|
4069
|
+
_panFromCenter(offset, constraintContext) {
|
|
4070
|
+
const { width, height } = this.getViewportProps();
|
|
4071
|
+
return this.pan(
|
|
4072
|
+
{
|
|
4073
|
+
startPos: [width / 2, height / 2],
|
|
4074
|
+
pos: [width / 2 + offset[0], height / 2 + offset[1]]
|
|
4075
|
+
},
|
|
4076
|
+
constraintContext
|
|
4077
|
+
);
|
|
4078
|
+
}
|
|
4079
|
+
_getUpdatedState(newProps, constraintContext) {
|
|
4080
|
+
return new this.constructor({
|
|
4081
|
+
makeViewport: this.makeViewport,
|
|
4082
|
+
...this.getViewportProps(),
|
|
4083
|
+
...this.getState(),
|
|
4084
|
+
...newProps,
|
|
4085
|
+
constraintContext
|
|
4086
|
+
});
|
|
4087
|
+
}
|
|
4088
|
+
_unproject(pos) {
|
|
4089
|
+
const viewport = this.makeViewport(this.getViewportProps());
|
|
4090
|
+
return pos && viewport.unproject(pos);
|
|
4091
|
+
}
|
|
4092
|
+
_unproject3D(pos, altitude) {
|
|
4093
|
+
const viewport = this.makeViewport(this.getViewportProps());
|
|
4094
|
+
return viewport.unproject(pos, { targetZ: altitude });
|
|
4095
|
+
}
|
|
4096
|
+
_getNewRotation(pos, startPos, startPitch, startBearing) {
|
|
4097
|
+
const deltaX = pos[0] - startPos[0];
|
|
4098
|
+
const deltaY = pos[1] - startPos[1];
|
|
4099
|
+
const centerY = pos[1];
|
|
4100
|
+
const startY = startPos[1];
|
|
4101
|
+
const { width, height } = this.getViewportProps();
|
|
4102
|
+
const deltaScaleX = deltaX / width;
|
|
4103
|
+
let deltaScaleY = 0;
|
|
4104
|
+
if (deltaY > 0) {
|
|
4105
|
+
if (Math.abs(height - startY) > PITCH_MOUSE_THRESHOLD) {
|
|
4106
|
+
deltaScaleY = deltaY / (startY - height) * PITCH_ACCEL;
|
|
4107
|
+
}
|
|
4108
|
+
} else if (deltaY < 0) {
|
|
4109
|
+
if (startY > PITCH_MOUSE_THRESHOLD) {
|
|
4110
|
+
deltaScaleY = 1 - centerY / startY;
|
|
4111
|
+
}
|
|
4112
|
+
}
|
|
4113
|
+
deltaScaleY = (0, import_core9.clamp)(deltaScaleY, -1, 1);
|
|
4114
|
+
const { minPitch, maxPitch } = this.getViewportProps();
|
|
4115
|
+
const bearing = startBearing + 180 * deltaScaleX;
|
|
4116
|
+
let pitch = startPitch;
|
|
4117
|
+
if (deltaScaleY > 0) {
|
|
4118
|
+
pitch = startPitch + deltaScaleY * (maxPitch - startPitch);
|
|
4119
|
+
} else if (deltaScaleY < 0) {
|
|
4120
|
+
pitch = startPitch - deltaScaleY * (minPitch - startPitch);
|
|
4121
|
+
}
|
|
4122
|
+
return {
|
|
4123
|
+
pitch,
|
|
4124
|
+
bearing
|
|
4125
|
+
};
|
|
4126
|
+
}
|
|
4127
|
+
};
|
|
4128
|
+
var MapController = class extends Controller {
|
|
4129
|
+
constructor() {
|
|
4130
|
+
super(...arguments);
|
|
4131
|
+
this.ControllerState = MapState;
|
|
4132
|
+
this.transition = {
|
|
4133
|
+
transitionDuration: 300,
|
|
4134
|
+
transitionInterpolator: new LinearInterpolator({
|
|
4135
|
+
transitionProps: {
|
|
4136
|
+
compare: ["longitude", "latitude", "zoom", "bearing", "pitch", "position"],
|
|
4137
|
+
required: ["longitude", "latitude", "zoom"]
|
|
4138
|
+
}
|
|
4139
|
+
})
|
|
4140
|
+
};
|
|
4141
|
+
this.dragMode = "pan";
|
|
4142
|
+
/**
|
|
4143
|
+
* Rotation pivot behavior:
|
|
4144
|
+
* - 'center': Rotate around viewport center (default)
|
|
4145
|
+
* - '2d': Rotate around pointer position at ground level (z=0)
|
|
4146
|
+
* - '3d': Rotate around 3D picked point (requires pickPosition callback)
|
|
4147
|
+
*/
|
|
4148
|
+
this.rotationPivot = "center";
|
|
4149
|
+
/** Add altitude to rotateStart params based on rotationPivot mode */
|
|
4150
|
+
this._getAltitude = (pos) => {
|
|
4151
|
+
if (this.rotationPivot === "2d") {
|
|
4152
|
+
return 0;
|
|
4153
|
+
} else if (this.rotationPivot === "3d") {
|
|
4154
|
+
if (this.pickPosition) {
|
|
4155
|
+
const { x, y } = this.props;
|
|
4156
|
+
const pickResult = this.pickPosition(x + pos[0], y + pos[1]);
|
|
4157
|
+
if (pickResult && pickResult.coordinate && pickResult.coordinate.length >= 3) {
|
|
4158
|
+
return pickResult.coordinate[2];
|
|
4159
|
+
}
|
|
4160
|
+
}
|
|
4161
|
+
}
|
|
4162
|
+
return void 0;
|
|
4163
|
+
};
|
|
4164
|
+
}
|
|
4165
|
+
setProps(props) {
|
|
4166
|
+
if ("rotationPivot" in props) {
|
|
4167
|
+
this.rotationPivot = props.rotationPivot || "center";
|
|
4168
|
+
}
|
|
4169
|
+
props.getAltitude = this._getAltitude;
|
|
4170
|
+
props.position = props.position || [0, 0, 0];
|
|
4171
|
+
props.maxBounds = props.maxBounds || (props.normalize === false ? null : WEB_MERCATOR_MAX_BOUNDS);
|
|
4172
|
+
super.setProps(props);
|
|
4173
|
+
}
|
|
4174
|
+
updateViewport(newControllerState, extraProps = null, interactionState = {}) {
|
|
4175
|
+
const state = newControllerState.getState();
|
|
4176
|
+
if (interactionState.isDragging && state.startRotateLngLat) {
|
|
4177
|
+
interactionState = {
|
|
4178
|
+
...interactionState,
|
|
4179
|
+
rotationPivotPosition: state.startRotateLngLat
|
|
4180
|
+
};
|
|
4181
|
+
} else if (interactionState.isDragging === false) {
|
|
4182
|
+
interactionState = { ...interactionState, rotationPivotPosition: void 0 };
|
|
4183
|
+
}
|
|
4184
|
+
super.updateViewport(newControllerState, extraProps, interactionState);
|
|
4185
|
+
}
|
|
4186
|
+
};
|
|
4187
|
+
|
|
4188
|
+
// ../core/src/views/map-view.ts
|
|
4189
|
+
var MapView = class extends View {
|
|
4190
|
+
constructor(props = {}) {
|
|
4191
|
+
super(props);
|
|
4192
|
+
}
|
|
4193
|
+
getViewportType() {
|
|
4194
|
+
return WebMercatorViewport;
|
|
4195
|
+
}
|
|
4196
|
+
get ControllerType() {
|
|
4197
|
+
return MapController;
|
|
4198
|
+
}
|
|
4199
|
+
};
|
|
4200
|
+
MapView.displayName = "MapView";
|
|
4201
|
+
|
|
4202
|
+
// ../core/src/passes/draw-layers-pass.ts
|
|
4203
|
+
var DrawLayersPass = class extends LayersPass {
|
|
4204
|
+
shouldDrawLayer(layer) {
|
|
4205
|
+
const { operation } = layer.props;
|
|
4206
|
+
return operation.includes("draw") || operation.includes("terrain");
|
|
4207
|
+
}
|
|
4208
|
+
render(options) {
|
|
4209
|
+
return this._render(options);
|
|
4210
|
+
}
|
|
4211
|
+
};
|
|
4212
|
+
|
|
4213
|
+
// ../core/src/lib/deck-renderer.ts
|
|
4214
|
+
var TRACE_RENDER_LAYERS = "deckRenderer.renderLayers";
|
|
4215
|
+
var DeckRenderer = class {
|
|
4216
|
+
constructor(device2, opts = {}) {
|
|
4217
|
+
this.device = device2;
|
|
4218
|
+
this.stats = opts.stats;
|
|
4219
|
+
this.layerFilter = null;
|
|
4220
|
+
this.drawPickingColors = false;
|
|
4221
|
+
this.drawLayersPass = new DrawLayersPass(device2);
|
|
4222
|
+
this.pickLayersPass = new PickLayersPass(device2);
|
|
4223
|
+
this.renderCount = 0;
|
|
4224
|
+
this._needsRedraw = "Initial render";
|
|
4225
|
+
this.renderBuffers = [];
|
|
4226
|
+
this.lastPostProcessEffect = null;
|
|
4227
|
+
}
|
|
4228
|
+
setProps(props) {
|
|
4229
|
+
if (this.layerFilter !== props.layerFilter) {
|
|
4230
|
+
this.layerFilter = props.layerFilter;
|
|
4231
|
+
this._needsRedraw = "layerFilter changed";
|
|
4232
|
+
}
|
|
4233
|
+
if (this.drawPickingColors !== props.drawPickingColors) {
|
|
4234
|
+
this.drawPickingColors = props.drawPickingColors;
|
|
4235
|
+
this._needsRedraw = "drawPickingColors changed";
|
|
4236
|
+
}
|
|
4237
|
+
}
|
|
4238
|
+
renderLayers(opts) {
|
|
4239
|
+
const layerPass = this.drawPickingColors ? this.pickLayersPass : this.drawLayersPass;
|
|
4240
|
+
const renderOpts = {
|
|
4241
|
+
layerFilter: this.layerFilter,
|
|
4242
|
+
isPicking: this.drawPickingColors,
|
|
4243
|
+
...opts
|
|
4244
|
+
};
|
|
4245
|
+
if (!opts.viewports.length) {
|
|
4246
|
+
const renderResult2 = layerPass.render(renderOpts);
|
|
4247
|
+
const renderStats2 = "stats" in renderResult2 ? renderResult2.stats : renderResult2;
|
|
4248
|
+
this._updateStats(renderStats2);
|
|
4249
|
+
return;
|
|
4250
|
+
}
|
|
4251
|
+
if (renderOpts.effects) {
|
|
4252
|
+
this._preRender(renderOpts.effects, renderOpts);
|
|
4253
|
+
}
|
|
4254
|
+
const outputBuffer = this.lastPostProcessEffect ? this.renderBuffers[0] : renderOpts.target;
|
|
4255
|
+
if (this.lastPostProcessEffect) {
|
|
4256
|
+
renderOpts.clearColor = [0, 0, 0, 0];
|
|
4257
|
+
renderOpts.clearCanvas = true;
|
|
4258
|
+
}
|
|
4259
|
+
const renderResult = layerPass.render({ ...renderOpts, target: outputBuffer });
|
|
4260
|
+
const renderStats = "stats" in renderResult ? renderResult.stats : renderResult;
|
|
4261
|
+
if (renderOpts.effects) {
|
|
4262
|
+
if (this.lastPostProcessEffect) {
|
|
4263
|
+
renderOpts.clearCanvas = opts.clearCanvas === void 0 ? true : opts.clearCanvas;
|
|
4264
|
+
}
|
|
4265
|
+
this._postRender(renderOpts.effects, renderOpts);
|
|
4266
|
+
}
|
|
4267
|
+
this.renderCount++;
|
|
4268
|
+
debug(TRACE_RENDER_LAYERS, this, renderStats, opts);
|
|
4269
|
+
this._updateStats(renderStats);
|
|
4270
|
+
}
|
|
4271
|
+
needsRedraw(opts = { clearRedrawFlags: false }) {
|
|
4272
|
+
const redraw = this._needsRedraw;
|
|
4273
|
+
if (opts.clearRedrawFlags) {
|
|
4274
|
+
this._needsRedraw = false;
|
|
4275
|
+
}
|
|
4276
|
+
return redraw;
|
|
4277
|
+
}
|
|
4278
|
+
finalize() {
|
|
4279
|
+
const { renderBuffers } = this;
|
|
4280
|
+
for (const buffer of renderBuffers) {
|
|
4281
|
+
buffer.delete();
|
|
4282
|
+
}
|
|
4283
|
+
renderBuffers.length = 0;
|
|
4284
|
+
}
|
|
4285
|
+
_updateStats(source2) {
|
|
4286
|
+
if (!this.stats) return;
|
|
4287
|
+
let layersCount = 0;
|
|
4288
|
+
for (const { visibleCount } of source2) {
|
|
4289
|
+
layersCount += visibleCount;
|
|
4290
|
+
}
|
|
4291
|
+
this.stats.get("Layers rendered").addCount(layersCount);
|
|
4292
|
+
}
|
|
4293
|
+
_preRender(effects, opts) {
|
|
4294
|
+
this.lastPostProcessEffect = null;
|
|
4295
|
+
opts.preRenderStats = opts.preRenderStats || {};
|
|
4296
|
+
for (const effect of effects) {
|
|
4297
|
+
opts.preRenderStats[effect.id] = effect.preRender(opts);
|
|
4298
|
+
if (effect.postRender) {
|
|
4299
|
+
this.lastPostProcessEffect = effect.id;
|
|
4300
|
+
}
|
|
4301
|
+
}
|
|
4302
|
+
if (this.lastPostProcessEffect) {
|
|
4303
|
+
this._resizeRenderBuffers(opts.canvasContext);
|
|
4304
|
+
}
|
|
4305
|
+
}
|
|
4306
|
+
_resizeRenderBuffers(canvasContext = this.device.canvasContext) {
|
|
4307
|
+
const { renderBuffers } = this;
|
|
4308
|
+
const size = canvasContext.getDrawingBufferSize();
|
|
4309
|
+
const [width, height] = size;
|
|
4310
|
+
if (renderBuffers.length === 0) {
|
|
4311
|
+
[0, 1].map((i) => {
|
|
4312
|
+
const texture = this.device.createTexture({
|
|
4313
|
+
sampler: { minFilter: "linear", magFilter: "linear" },
|
|
4314
|
+
width,
|
|
4315
|
+
height
|
|
4316
|
+
});
|
|
4317
|
+
renderBuffers.push(
|
|
4318
|
+
this.device.createFramebuffer({
|
|
4319
|
+
id: `deck-renderbuffer-${i}`,
|
|
4320
|
+
colorAttachments: [texture]
|
|
4321
|
+
})
|
|
4322
|
+
);
|
|
4323
|
+
});
|
|
4324
|
+
}
|
|
4325
|
+
for (const buffer of renderBuffers) {
|
|
4326
|
+
buffer.resize(size);
|
|
4327
|
+
}
|
|
4328
|
+
}
|
|
4329
|
+
_postRender(effects, opts) {
|
|
4330
|
+
var _a2;
|
|
4331
|
+
const { renderBuffers } = this;
|
|
4332
|
+
const target = opts.target ?? ((_a2 = opts.canvasContext) == null ? void 0 : _a2.getCurrentFramebuffer()) ?? opts.target;
|
|
4333
|
+
const params = {
|
|
4334
|
+
...opts,
|
|
4335
|
+
inputBuffer: renderBuffers[0],
|
|
4336
|
+
swapBuffer: renderBuffers[1]
|
|
4337
|
+
};
|
|
4338
|
+
for (const effect of effects) {
|
|
4339
|
+
if (effect.postRender) {
|
|
4340
|
+
params.target = effect.id === this.lastPostProcessEffect ? target : void 0;
|
|
4341
|
+
const buffer = effect.postRender(params);
|
|
4342
|
+
params.inputBuffer = buffer;
|
|
4343
|
+
params.swapBuffer = buffer === renderBuffers[0] ? renderBuffers[1] : renderBuffers[0];
|
|
4344
|
+
}
|
|
4345
|
+
}
|
|
4346
|
+
}
|
|
4347
|
+
};
|
|
4348
|
+
|
|
4349
|
+
// ../core/src/utils/count.ts
|
|
4350
|
+
var ERR_NOT_OBJECT = "count(): argument not an object";
|
|
4351
|
+
var ERR_NOT_CONTAINER = "count(): argument not a container";
|
|
4352
|
+
function count(container) {
|
|
4353
|
+
if (!isObject(container)) {
|
|
4354
|
+
throw new Error(ERR_NOT_OBJECT);
|
|
4355
|
+
}
|
|
4356
|
+
if (typeof container.count === "function") {
|
|
4357
|
+
return container.count();
|
|
4358
|
+
}
|
|
4359
|
+
if (Number.isFinite(container.size)) {
|
|
4360
|
+
return container.size;
|
|
4361
|
+
}
|
|
4362
|
+
if (Number.isFinite(container.length)) {
|
|
4363
|
+
return container.length;
|
|
4364
|
+
}
|
|
4365
|
+
if (isPlainObject(container)) {
|
|
4366
|
+
return Object.keys(container).length;
|
|
4367
|
+
}
|
|
4368
|
+
throw new Error(ERR_NOT_CONTAINER);
|
|
4369
|
+
}
|
|
4370
|
+
function isPlainObject(value) {
|
|
4371
|
+
return value !== null && typeof value === "object" && value.constructor === Object;
|
|
4372
|
+
}
|
|
4373
|
+
function isObject(value) {
|
|
4374
|
+
return value !== null && typeof value === "object";
|
|
4375
|
+
}
|
|
38
4376
|
|
|
39
4377
|
// dist/utils/setup-gl.js
|
|
40
4378
|
var import_test_utils = require("@luma.gl/test-utils");
|
|
@@ -44,7 +4382,7 @@ var gl = ((_a = import_test_utils.webglDevice) == null ? void 0 : _a.gl) || 1;
|
|
|
44
4382
|
globalThis.glContext = globalThis.glContext || gl;
|
|
45
4383
|
|
|
46
4384
|
// dist/lifecycle-test.js
|
|
47
|
-
var testViewport = new
|
|
4385
|
+
var testViewport = new MapView({}).makeViewport({
|
|
48
4386
|
width: 100,
|
|
49
4387
|
height: 100,
|
|
50
4388
|
viewState: { longitude: 0, latitude: 0, zoom: 1 }
|
|
@@ -55,7 +4393,7 @@ function defaultOnError(error, title) {
|
|
|
55
4393
|
}
|
|
56
4394
|
}
|
|
57
4395
|
function initializeLayerManager({ layer, viewport = testViewport, onError = defaultOnError }) {
|
|
58
|
-
const layerManager = new
|
|
4396
|
+
const layerManager = new LayerManager(device, { viewport });
|
|
59
4397
|
layerManager.setProps({
|
|
60
4398
|
onError: (error) => onError(error, `initializing ${layer.id}`)
|
|
61
4399
|
});
|
|
@@ -74,7 +4412,7 @@ function testInitializeLayer(opts) {
|
|
|
74
4412
|
}
|
|
75
4413
|
async function testInitializeLayerAsync(opts) {
|
|
76
4414
|
const layerManager = initializeLayerManager(opts);
|
|
77
|
-
const deckRenderer = new
|
|
4415
|
+
const deckRenderer = new DeckRenderer(device);
|
|
78
4416
|
while (!opts.layer.isLoaded) {
|
|
79
4417
|
await update({ layerManager, deckRenderer, oldResourceCounts: {} });
|
|
80
4418
|
}
|
|
@@ -124,8 +4462,8 @@ async function testLayerAsync(opts) {
|
|
|
124
4462
|
}
|
|
125
4463
|
function setupLayerTests(testTitle, { viewport = testViewport, timeline, onError = defaultOnError }) {
|
|
126
4464
|
const oldResourceCounts = getResourceCounts();
|
|
127
|
-
const layerManager = new
|
|
128
|
-
const deckRenderer = new
|
|
4465
|
+
const layerManager = new LayerManager(device, { viewport, timeline });
|
|
4466
|
+
const deckRenderer = new DeckRenderer(device);
|
|
129
4467
|
const props = {
|
|
130
4468
|
layerFilter: null,
|
|
131
4469
|
drawPickingColors: false,
|
|
@@ -267,8 +4605,7 @@ function toLowPrecision(input, precision = 11) {
|
|
|
267
4605
|
}
|
|
268
4606
|
|
|
269
4607
|
// dist/generate-layer-tests.js
|
|
270
|
-
|
|
271
|
-
function noop() {
|
|
4608
|
+
function noop2() {
|
|
272
4609
|
}
|
|
273
4610
|
function defaultAssert(condition, comment) {
|
|
274
4611
|
if (!condition) {
|
|
@@ -279,12 +4616,12 @@ function generateLayerTests({
|
|
|
279
4616
|
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
280
4617
|
Layer,
|
|
281
4618
|
sampleProps = {},
|
|
282
|
-
assert = defaultAssert,
|
|
283
|
-
onBeforeUpdate =
|
|
284
|
-
onAfterUpdate =
|
|
4619
|
+
assert: assert2 = defaultAssert,
|
|
4620
|
+
onBeforeUpdate = noop2,
|
|
4621
|
+
onAfterUpdate = noop2,
|
|
285
4622
|
runDefaultAsserts = true
|
|
286
4623
|
}) {
|
|
287
|
-
|
|
4624
|
+
assert2(Layer.layerName, "Layer should have display name");
|
|
288
4625
|
function wrapTestCaseTitle(title) {
|
|
289
4626
|
return `${Layer.layerName}#${title}`;
|
|
290
4627
|
}
|
|
@@ -306,20 +4643,20 @@ function generateLayerTests({
|
|
|
306
4643
|
try {
|
|
307
4644
|
new Layer({});
|
|
308
4645
|
} catch (error) {
|
|
309
|
-
|
|
4646
|
+
assert2(false, `Construct ${Layer.layerName} throws: ${error.message}`);
|
|
310
4647
|
}
|
|
311
4648
|
const { _propTypes: propTypes, _mergedDefaultProps: defaultProps } = Layer;
|
|
312
4649
|
testCases.push(...makeAltDataTestCases(sampleProps, propTypes));
|
|
313
4650
|
for (const propName in Layer.defaultProps) {
|
|
314
4651
|
if (!(propName in sampleProps)) {
|
|
315
|
-
const newTestCase = makeAltPropTestCase({ propName, propTypes, defaultProps, sampleProps, assert }) || [];
|
|
4652
|
+
const newTestCase = makeAltPropTestCase({ propName, propTypes, defaultProps, sampleProps, assert: assert2 }) || [];
|
|
316
4653
|
testCases.push(...newTestCase);
|
|
317
4654
|
}
|
|
318
4655
|
}
|
|
319
4656
|
testCases.forEach((testCase) => {
|
|
320
4657
|
testCase.title = wrapTestCaseTitle(testCase.title);
|
|
321
|
-
const beforeFunc = testCase.onBeforeUpdate ||
|
|
322
|
-
const afterFunc = testCase.onAfterUpdate ||
|
|
4658
|
+
const beforeFunc = testCase.onBeforeUpdate || noop2;
|
|
4659
|
+
const afterFunc = testCase.onAfterUpdate || noop2;
|
|
323
4660
|
testCase.onBeforeUpdate = (params) => {
|
|
324
4661
|
beforeFunc(params);
|
|
325
4662
|
onBeforeUpdate(params);
|
|
@@ -330,18 +4667,18 @@ function generateLayerTests({
|
|
|
330
4667
|
if (runDefaultAsserts) {
|
|
331
4668
|
if (params.layer.isComposite) {
|
|
332
4669
|
const { data } = params.layer.props;
|
|
333
|
-
if (data && typeof data === "object" && (
|
|
334
|
-
|
|
4670
|
+
if (data && typeof data === "object" && count(data)) {
|
|
4671
|
+
assert2(params.subLayers.length, "Layer should have sublayers");
|
|
335
4672
|
}
|
|
336
4673
|
} else {
|
|
337
|
-
|
|
4674
|
+
assert2(params.layer.getModels().length, "Layer should have models");
|
|
338
4675
|
}
|
|
339
4676
|
}
|
|
340
4677
|
};
|
|
341
4678
|
});
|
|
342
4679
|
return testCases;
|
|
343
4680
|
}
|
|
344
|
-
function makeAltPropTestCase({ propName, propTypes, defaultProps, sampleProps, assert }) {
|
|
4681
|
+
function makeAltPropTestCase({ propName, propTypes, defaultProps, sampleProps, assert: assert2 }) {
|
|
345
4682
|
const newProps = { ...sampleProps };
|
|
346
4683
|
const propDef = propTypes[propName];
|
|
347
4684
|
if (!propDef) {
|
|
@@ -383,7 +4720,7 @@ function makeAltPropTestCase({ propName, propTypes, defaultProps, sampleProps, a
|
|
|
383
4720
|
[propName]: "function"
|
|
384
4721
|
};
|
|
385
4722
|
const onBeforeUpdate = () => callCount = 0;
|
|
386
|
-
const onAfterUpdate = () =>
|
|
4723
|
+
const onAfterUpdate = () => assert2(callCount > 0, "accessor function is called");
|
|
387
4724
|
return [
|
|
388
4725
|
{
|
|
389
4726
|
title: `${propName}: () => ${defaultProps[propName]}`,
|