@deck.gl/test-utils 9.4.0-alpha.2 → 9.4.0-beta.3

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/vitest.cjs CHANGED
@@ -15,7 +15,7 @@ var __copyProps = (to, from, except, desc) => {
15
15
  }
16
16
  return to;
17
17
  };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
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,4186 @@ __export(vitest_exports, {
33
33
  module.exports = __toCommonJS(vitest_exports);
34
34
  var import_vitest = require("vitest");
35
35
 
36
- // dist/lifecycle-test.js
37
- var import_core = require("@deck.gl/core");
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 their dimensions, common-space scale, and
890
+ // 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 && viewport.projectionMode === this.projectionMode && viewport.resolution === this.resolution && (0, import_core2.equals)(viewport.distanceScales.unitsPerMeter, this.distanceScales.unitsPerMeter) && (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
+ /** Returns whether two Web Mercator viewports use the same projection settings. */
1236
+ equals(viewport) {
1237
+ return viewport instanceof _WebMercatorViewport && viewport._pseudoMeters === this._pseudoMeters && super.equals(viewport);
1238
+ }
1239
+ projectPosition(xyz) {
1240
+ if (this._pseudoMeters) {
1241
+ return super.projectPosition(xyz);
1242
+ }
1243
+ const [X, Y] = this.projectFlat(xyz);
1244
+ const Z = (xyz[2] || 0) * (0, import_web_mercator2.unitsPerMeter)(xyz[1]);
1245
+ return [X, Y, Z];
1246
+ }
1247
+ unprojectPosition(xyz) {
1248
+ if (this._pseudoMeters) {
1249
+ return super.unprojectPosition(xyz);
1250
+ }
1251
+ const [X, Y] = this.unprojectFlat(xyz);
1252
+ const Z = (xyz[2] || 0) / (0, import_web_mercator2.unitsPerMeter)(Y);
1253
+ return [X, Y, Z];
1254
+ }
1255
+ /**
1256
+ * Add a meter delta to a base lnglat coordinate, returning a new lnglat array
1257
+ *
1258
+ * Note: Uses simple linear approximation around the viewport center
1259
+ * Error increases with size of offset (roughly 1% per 100km)
1260
+ *
1261
+ * @param {[Number,Number]|[Number,Number,Number]) lngLatZ - base coordinate
1262
+ * @param {[Number,Number]|[Number,Number,Number]) xyz - array of meter deltas
1263
+ * @return {[Number,Number]|[Number,Number,Number]) array of [lng,lat,z] deltas
1264
+ */
1265
+ addMetersToLngLat(lngLatZ, xyz) {
1266
+ return (0, import_web_mercator2.addMetersToLngLat)(lngLatZ, xyz);
1267
+ }
1268
+ panByPosition(coords, pixel, startPixel) {
1269
+ const fromLocation = (0, import_web_mercator2.pixelsToWorld)(pixel, this.pixelUnprojectionMatrix);
1270
+ const toLocation = this.projectFlat(coords);
1271
+ const translate = import_core3.vec2.add([], toLocation, import_core3.vec2.negate([], fromLocation));
1272
+ const newCenter = import_core3.vec2.add([], this.center, translate);
1273
+ const [longitude, latitude] = this.unprojectFlat(newCenter);
1274
+ return { longitude, latitude };
1275
+ }
1276
+ /**
1277
+ * Returns a new longitude and latitude that keeps a 3D world coordinate at a given screen pixel
1278
+ * This version handles the z-component (altitude) properly for cameras positioned above ground
1279
+ */
1280
+ panByPosition3D(coords, pixel) {
1281
+ const targetZ = coords[2] || 0;
1282
+ const deltaLngLat = import_core3.vec2.sub([], coords, this.unproject(pixel, { targetZ }));
1283
+ return { longitude: this.longitude + deltaLngLat[0], latitude: this.latitude + deltaLngLat[1] };
1284
+ }
1285
+ getBounds(options = {}) {
1286
+ const corners = (0, import_web_mercator2.getBounds)(this, options.z || 0);
1287
+ return [
1288
+ Math.min(corners[0][0], corners[1][0], corners[2][0], corners[3][0]),
1289
+ Math.min(corners[0][1], corners[1][1], corners[2][1], corners[3][1]),
1290
+ Math.max(corners[0][0], corners[1][0], corners[2][0], corners[3][0]),
1291
+ Math.max(corners[0][1], corners[1][1], corners[2][1], corners[3][1])
1292
+ ];
1293
+ }
1294
+ /**
1295
+ * Returns a new viewport that fit around the given rectangle.
1296
+ * Only supports non-perspective mode.
1297
+ */
1298
+ fitBounds(bounds, options = {}) {
1299
+ const { width, height } = this;
1300
+ const { longitude, latitude, zoom } = (0, import_web_mercator2.fitBounds)({ width, height, bounds, ...options });
1301
+ return new _WebMercatorViewport({ width, height, longitude, latitude, zoom });
1302
+ }
1303
+ };
1304
+ _WebMercatorViewport.displayName = "WebMercatorViewport";
1305
+ var WebMercatorViewport = _WebMercatorViewport;
1306
+
1307
+ // ../core/src/passes/pick-layers-pass.ts
1308
+ var PICKING_BLENDING = {
1309
+ blendColorOperation: "add",
1310
+ blendColorSrcFactor: "one",
1311
+ blendColorDstFactor: "zero",
1312
+ blendAlphaOperation: "add",
1313
+ blendAlphaSrcFactor: "constant",
1314
+ blendAlphaDstFactor: "zero"
1315
+ };
1316
+ var PickLayersPass = class extends LayersPass {
1317
+ constructor() {
1318
+ super(...arguments);
1319
+ this._colorEncoderState = null;
1320
+ }
1321
+ render(props) {
1322
+ if ("pickingFBO" in props) {
1323
+ return this._drawPickingBuffer(props);
1324
+ }
1325
+ const stats = super._render(props);
1326
+ return { decodePickingColor: null, stats };
1327
+ }
1328
+ // Private
1329
+ // Draws list of layers and viewports into the picking buffer
1330
+ // Note: does not sample the buffer, that has to be done by the caller
1331
+ _drawPickingBuffer({
1332
+ layers,
1333
+ layerFilter,
1334
+ views,
1335
+ viewports,
1336
+ onViewportActive,
1337
+ pickingFBO,
1338
+ deviceRect: { x, y, width, height },
1339
+ cullRect,
1340
+ effects,
1341
+ pass = "picking",
1342
+ pickZ,
1343
+ canvasContext,
1344
+ shaderModuleProps,
1345
+ clearColor
1346
+ }) {
1347
+ this.pickZ = pickZ;
1348
+ const colorEncoderState = this._resetColorEncoder(pickZ);
1349
+ const scissorRect = [x, y, width, height];
1350
+ const renderStatus = super._render({
1351
+ target: pickingFBO,
1352
+ layers,
1353
+ layerFilter,
1354
+ views,
1355
+ viewports,
1356
+ onViewportActive,
1357
+ cullRect,
1358
+ effects: effects == null ? void 0 : effects.filter((e) => e.useInPicking),
1359
+ pass,
1360
+ canvasContext,
1361
+ isPicking: true,
1362
+ shaderModuleProps,
1363
+ clearColor: clearColor ?? [0, 0, 0, 0],
1364
+ colorMask: 15,
1365
+ scissorRect
1366
+ });
1367
+ this._colorEncoderState = null;
1368
+ const decodePickingColor = colorEncoderState && decodeColor.bind(null, colorEncoderState);
1369
+ return { decodePickingColor, stats: renderStatus };
1370
+ }
1371
+ shouldDrawLayer(layer) {
1372
+ const { pickable, operation } = layer.props;
1373
+ return pickable && operation.includes("draw") || operation.includes("terrain") || operation.includes("mask");
1374
+ }
1375
+ getShaderModuleProps(layer, effects, otherShaderModuleProps) {
1376
+ var _a2;
1377
+ return {
1378
+ picking: {
1379
+ isActive: 1,
1380
+ isAttribute: this.pickZ,
1381
+ disabledPickingIndices: (_a2 = layer.internalState) == null ? void 0 : _a2.disabledPickingIndices
1382
+ },
1383
+ lighting: { enabled: false }
1384
+ };
1385
+ }
1386
+ getLayerParameters(layer, layerIndex, viewport) {
1387
+ var _a2;
1388
+ const pickParameters = {
1389
+ ...layer.props.parameters
1390
+ };
1391
+ const { pickable, operation } = layer.props;
1392
+ if (!this._colorEncoderState) {
1393
+ pickParameters.blend = false;
1394
+ } else if (pickable && operation.includes("draw")) {
1395
+ Object.assign(pickParameters, PICKING_BLENDING);
1396
+ pickParameters.blend = true;
1397
+ if (this.device.type === "webgpu") {
1398
+ pickParameters.blendConstant = encodeColor(this._colorEncoderState, layer, viewport);
1399
+ } else {
1400
+ pickParameters.blendColor = encodeColor(this._colorEncoderState, layer, viewport);
1401
+ }
1402
+ if (operation.includes("terrain") && ((_a2 = layer.state) == null ? void 0 : _a2._hasPickingCover)) {
1403
+ pickParameters.blendAlphaSrcFactor = "one";
1404
+ }
1405
+ } else if (operation.includes("terrain")) {
1406
+ pickParameters.blend = false;
1407
+ }
1408
+ return pickParameters;
1409
+ }
1410
+ _resetColorEncoder(pickZ) {
1411
+ this._colorEncoderState = pickZ ? null : {
1412
+ byLayer: /* @__PURE__ */ new Map(),
1413
+ byAlpha: []
1414
+ };
1415
+ return this._colorEncoderState;
1416
+ }
1417
+ };
1418
+ function encodeColor(encoded, layer, viewport) {
1419
+ const { byLayer, byAlpha } = encoded;
1420
+ let a;
1421
+ let entry = byLayer.get(layer);
1422
+ if (entry) {
1423
+ entry.viewports.push(viewport);
1424
+ a = entry.a;
1425
+ } else {
1426
+ a = byLayer.size + 1;
1427
+ if (a <= 255) {
1428
+ entry = { a, layer, viewports: [viewport] };
1429
+ byLayer.set(layer, entry);
1430
+ byAlpha[a] = entry;
1431
+ } else {
1432
+ log_default.warn("Too many pickable layers, only picking the first 255")();
1433
+ a = 0;
1434
+ }
1435
+ }
1436
+ return [0, 0, 0, a / 255];
1437
+ }
1438
+ function decodeColor(encoded, pickedColor) {
1439
+ const entry = encoded.byAlpha[pickedColor[3]];
1440
+ return entry && {
1441
+ pickedLayer: entry.layer,
1442
+ pickedViewports: entry.viewports,
1443
+ pickedObjectIndex: entry.layer.decodePickingColor(pickedColor)
1444
+ };
1445
+ }
1446
+
1447
+ // ../core/src/lib/layer-manager.ts
1448
+ var import_engine = require("@luma.gl/engine");
1449
+
1450
+ // ../core/src/lifecycle/constants.ts
1451
+ var LIFECYCLE = {
1452
+ NO_STATE: "Awaiting state",
1453
+ MATCHED: "Matched. State transferred from previous layer",
1454
+ INITIALIZED: "Initialized",
1455
+ AWAITING_GC: "Discarded. Awaiting garbage collection",
1456
+ AWAITING_FINALIZATION: "No longer matched. Awaiting garbage collection",
1457
+ FINALIZED: "Finalized! Awaiting garbage collection"
1458
+ };
1459
+
1460
+ // ../core/src/utils/flatten.ts
1461
+ function flatten(array, filter = () => true) {
1462
+ if (!Array.isArray(array)) {
1463
+ return filter(array) ? [array] : [];
1464
+ }
1465
+ return flattenArray(array, filter, []);
1466
+ }
1467
+ function flattenArray(array, filter, result) {
1468
+ let index = -1;
1469
+ while (++index < array.length) {
1470
+ const value = array[index];
1471
+ if (Array.isArray(value)) {
1472
+ flattenArray(value, filter, result);
1473
+ } else if (filter(value)) {
1474
+ result.push(value);
1475
+ }
1476
+ }
1477
+ return result;
1478
+ }
1479
+
1480
+ // ../core/src/lib/layer-manager.ts
1481
+ var import_stats = require("@probe.gl/stats");
1482
+
1483
+ // ../core/src/lib/resource/resource.ts
1484
+ var import_core4 = require("@loaders.gl/core");
1485
+ var Resource = class {
1486
+ constructor(id, data, context) {
1487
+ this._loadCount = 0;
1488
+ this._subscribers = /* @__PURE__ */ new Set();
1489
+ this.id = id;
1490
+ this.context = context;
1491
+ this.setData(data);
1492
+ }
1493
+ // consumer: {onChange: Function}
1494
+ subscribe(consumer) {
1495
+ this._subscribers.add(consumer);
1496
+ }
1497
+ unsubscribe(consumer) {
1498
+ this._subscribers.delete(consumer);
1499
+ }
1500
+ inUse() {
1501
+ return this._subscribers.size > 0;
1502
+ }
1503
+ delete() {
1504
+ }
1505
+ getData() {
1506
+ return this.isLoaded ? this._error ? Promise.reject(this._error) : this._content : this._loader.then(() => this.getData());
1507
+ }
1508
+ setData(data, forceUpdate) {
1509
+ if (data === this._data && !forceUpdate) {
1510
+ return;
1511
+ }
1512
+ this._data = data;
1513
+ const loadCount = ++this._loadCount;
1514
+ let loader = data;
1515
+ if (typeof data === "string") {
1516
+ loader = (0, import_core4.load)(data);
1517
+ }
1518
+ if (loader instanceof Promise) {
1519
+ this.isLoaded = false;
1520
+ this._loader = loader.then((result) => {
1521
+ if (this._loadCount === loadCount) {
1522
+ this.isLoaded = true;
1523
+ this._error = void 0;
1524
+ this._content = result;
1525
+ }
1526
+ }).catch((error) => {
1527
+ if (this._loadCount === loadCount) {
1528
+ this.isLoaded = true;
1529
+ this._error = error || true;
1530
+ }
1531
+ });
1532
+ } else {
1533
+ this.isLoaded = true;
1534
+ this._error = void 0;
1535
+ this._content = data;
1536
+ }
1537
+ for (const subscriber of this._subscribers) {
1538
+ subscriber.onChange(this.getData());
1539
+ }
1540
+ }
1541
+ };
1542
+
1543
+ // ../core/src/lib/resource/resource-manager.ts
1544
+ var ResourceManager = class {
1545
+ constructor(props) {
1546
+ var _a2;
1547
+ this.protocol = props.protocol || "resource://";
1548
+ this._context = {
1549
+ device: props.device,
1550
+ // @ts-expect-error
1551
+ gl: (_a2 = props.device) == null ? void 0 : _a2.gl,
1552
+ resourceManager: this
1553
+ };
1554
+ this._resources = {};
1555
+ this._consumers = {};
1556
+ this._pruneRequest = null;
1557
+ }
1558
+ contains(resourceId) {
1559
+ if (resourceId.startsWith(this.protocol)) {
1560
+ return true;
1561
+ }
1562
+ return resourceId in this._resources;
1563
+ }
1564
+ add({
1565
+ resourceId,
1566
+ data,
1567
+ forceUpdate = false,
1568
+ persistent = true
1569
+ }) {
1570
+ let res = this._resources[resourceId];
1571
+ if (res) {
1572
+ res.setData(data, forceUpdate);
1573
+ } else {
1574
+ res = new Resource(resourceId, data, this._context);
1575
+ this._resources[resourceId] = res;
1576
+ }
1577
+ res.persistent = persistent;
1578
+ }
1579
+ remove(resourceId) {
1580
+ const res = this._resources[resourceId];
1581
+ if (res) {
1582
+ res.delete();
1583
+ delete this._resources[resourceId];
1584
+ }
1585
+ }
1586
+ unsubscribe({ consumerId }) {
1587
+ const consumer = this._consumers[consumerId];
1588
+ if (consumer) {
1589
+ for (const requestId in consumer) {
1590
+ const request = consumer[requestId];
1591
+ const resource = this._resources[request.resourceId];
1592
+ if (resource) {
1593
+ resource.unsubscribe(request);
1594
+ }
1595
+ }
1596
+ delete this._consumers[consumerId];
1597
+ this.prune();
1598
+ }
1599
+ }
1600
+ subscribe({
1601
+ resourceId,
1602
+ onChange,
1603
+ consumerId,
1604
+ requestId = "default"
1605
+ }) {
1606
+ const { _resources: resources, protocol } = this;
1607
+ if (resourceId.startsWith(protocol)) {
1608
+ resourceId = resourceId.replace(protocol, "");
1609
+ if (!resources[resourceId]) {
1610
+ this.add({ resourceId, data: null, persistent: false });
1611
+ }
1612
+ }
1613
+ const res = resources[resourceId];
1614
+ this._track(consumerId, requestId, res, onChange);
1615
+ if (res) {
1616
+ return res.getData();
1617
+ }
1618
+ return void 0;
1619
+ }
1620
+ prune() {
1621
+ if (!this._pruneRequest) {
1622
+ this._pruneRequest = setTimeout(() => this._prune(), 0);
1623
+ }
1624
+ }
1625
+ finalize() {
1626
+ for (const key in this._resources) {
1627
+ this._resources[key].delete();
1628
+ }
1629
+ }
1630
+ _track(consumerId, requestId, resource, onChange) {
1631
+ const consumers = this._consumers;
1632
+ const consumer = consumers[consumerId] = consumers[consumerId] || {};
1633
+ let request = consumer[requestId];
1634
+ const oldResource = request && request.resourceId && this._resources[request.resourceId];
1635
+ if (oldResource) {
1636
+ oldResource.unsubscribe(request);
1637
+ this.prune();
1638
+ }
1639
+ if (resource) {
1640
+ if (request) {
1641
+ request.onChange = onChange;
1642
+ request.resourceId = resource.id;
1643
+ } else {
1644
+ request = {
1645
+ onChange,
1646
+ resourceId: resource.id
1647
+ };
1648
+ }
1649
+ consumer[requestId] = request;
1650
+ resource.subscribe(request);
1651
+ }
1652
+ }
1653
+ _prune() {
1654
+ this._pruneRequest = null;
1655
+ for (const key of Object.keys(this._resources)) {
1656
+ const res = this._resources[key];
1657
+ if (!res.persistent && !res.inUse()) {
1658
+ res.delete();
1659
+ delete this._resources[key];
1660
+ }
1661
+ }
1662
+ }
1663
+ };
1664
+
1665
+ // ../core/src/lib/layer-manager.ts
1666
+ var TRACE_SET_LAYERS = "layerManager.setLayers";
1667
+ var TRACE_ACTIVATE_VIEWPORT = "layerManager.activateViewport";
1668
+ var LayerManager = class {
1669
+ /**
1670
+ * @param device
1671
+ * @param param1
1672
+ */
1673
+ // eslint-disable-next-line
1674
+ constructor(device2, props) {
1675
+ this._lastRenderedLayers = [];
1676
+ this._needsRedraw = false;
1677
+ this._needsUpdate = false;
1678
+ this._nextLayers = null;
1679
+ this._debug = false;
1680
+ // This flag is separate from _needsUpdate because it can be set during an update and should trigger another full update
1681
+ this._defaultShaderModulesChanged = false;
1682
+ //
1683
+ // INTERNAL METHODS
1684
+ //
1685
+ /** Make a viewport "current" in layer context, updating viewportChanged flags */
1686
+ this.activateViewport = (viewport) => {
1687
+ debug(TRACE_ACTIVATE_VIEWPORT, this, viewport);
1688
+ if (viewport) {
1689
+ this.context.viewport = viewport;
1690
+ }
1691
+ };
1692
+ var _a2;
1693
+ const { deck, stats, viewport, timeline } = props || {};
1694
+ this.layers = [];
1695
+ this.resourceManager = new ResourceManager({ device: device2, protocol: "deck://" });
1696
+ this.context = {
1697
+ mousePosition: null,
1698
+ userData: {},
1699
+ layerManager: this,
1700
+ device: device2,
1701
+ // @ts-expect-error
1702
+ gl: device2 == null ? void 0 : device2.gl,
1703
+ deck,
1704
+ shaderAssembler: getShaderAssembler(((_a2 = device2 == null ? void 0 : device2.info) == null ? void 0 : _a2.shadingLanguage) || "glsl"),
1705
+ defaultShaderModules: [layerUniforms],
1706
+ renderPass: void 0,
1707
+ stats: stats || new import_stats.Stats({ id: "deck.gl" }),
1708
+ // Make sure context.viewport is not empty on the first layer initialization
1709
+ viewport: viewport || new Viewport({ id: "DEFAULT-INITIAL-VIEWPORT" }),
1710
+ // Current viewport, exposed to layers for project* function
1711
+ timeline: timeline || new import_engine.Timeline(),
1712
+ resourceManager: this.resourceManager,
1713
+ onError: void 0
1714
+ };
1715
+ Object.seal(this);
1716
+ }
1717
+ /** Method to call when the layer manager is not needed anymore. */
1718
+ finalize() {
1719
+ this.resourceManager.finalize();
1720
+ for (const layer of this.layers) {
1721
+ this._finalizeLayer(layer);
1722
+ }
1723
+ }
1724
+ /** Check if a redraw is needed */
1725
+ needsRedraw(opts = { clearRedrawFlags: false }) {
1726
+ let redraw = this._needsRedraw;
1727
+ if (opts.clearRedrawFlags) {
1728
+ this._needsRedraw = false;
1729
+ }
1730
+ for (const layer of this.layers) {
1731
+ const layerNeedsRedraw = layer.getNeedsRedraw(opts);
1732
+ redraw = redraw || layerNeedsRedraw;
1733
+ }
1734
+ return redraw;
1735
+ }
1736
+ /** Check if a deep update of all layers is needed */
1737
+ needsUpdate() {
1738
+ if (this._nextLayers && this._nextLayers !== this._lastRenderedLayers) {
1739
+ return "layers changed";
1740
+ }
1741
+ if (this._defaultShaderModulesChanged) {
1742
+ return "shader modules changed";
1743
+ }
1744
+ return this._needsUpdate;
1745
+ }
1746
+ /** Layers will be redrawn (in next animation frame) */
1747
+ setNeedsRedraw(reason) {
1748
+ this._needsRedraw = this._needsRedraw || reason;
1749
+ }
1750
+ /** Layers will be updated deeply (in next animation frame)
1751
+ Potentially regenerating attributes and sub layers */
1752
+ setNeedsUpdate(reason) {
1753
+ this._needsUpdate = this._needsUpdate || reason;
1754
+ }
1755
+ /** Gets a list of currently rendered layers. Optionally filter by id. */
1756
+ getLayers({ layerIds } = {}) {
1757
+ return layerIds ? this.layers.filter((layer) => layerIds.find((layerId) => layer.id.indexOf(layerId) === 0)) : this.layers;
1758
+ }
1759
+ /** Set props needed for layer rendering and picking. */
1760
+ setProps(props) {
1761
+ if ("debug" in props) {
1762
+ this._debug = props.debug;
1763
+ }
1764
+ if ("userData" in props) {
1765
+ this.context.userData = props.userData;
1766
+ }
1767
+ if ("layers" in props) {
1768
+ this._nextLayers = props.layers;
1769
+ }
1770
+ if ("onError" in props) {
1771
+ this.context.onError = props.onError;
1772
+ }
1773
+ }
1774
+ /** Supply a new layer list, initiating sublayer generation and layer matching */
1775
+ setLayers(newLayers, reason) {
1776
+ debug(TRACE_SET_LAYERS, this, reason, newLayers);
1777
+ this._lastRenderedLayers = newLayers;
1778
+ const flatLayers = flatten(newLayers, Boolean);
1779
+ for (const layer of flatLayers) {
1780
+ layer.context = this.context;
1781
+ }
1782
+ this._updateLayers(this.layers, flatLayers);
1783
+ }
1784
+ /** Update layers from last cycle if `setNeedsUpdate()` has been called */
1785
+ updateLayers() {
1786
+ const reason = this.needsUpdate();
1787
+ if (reason) {
1788
+ this.setNeedsRedraw(`updating layers: ${reason}`);
1789
+ this.setLayers(this._nextLayers || this._lastRenderedLayers, reason);
1790
+ }
1791
+ this._nextLayers = null;
1792
+ }
1793
+ /** Register a default shader module */
1794
+ addDefaultShaderModule(module2) {
1795
+ const { defaultShaderModules } = this.context;
1796
+ if (!defaultShaderModules.find((m) => m.name === module2.name)) {
1797
+ defaultShaderModules.push(module2);
1798
+ this._defaultShaderModulesChanged = true;
1799
+ }
1800
+ }
1801
+ /** Deregister a default shader module */
1802
+ removeDefaultShaderModule(module2) {
1803
+ const { defaultShaderModules } = this.context;
1804
+ const i = defaultShaderModules.findIndex((m) => m.name === module2.name);
1805
+ if (i >= 0) {
1806
+ defaultShaderModules.splice(i, 1);
1807
+ this._defaultShaderModulesChanged = true;
1808
+ }
1809
+ }
1810
+ _handleError(stage, error, layer) {
1811
+ layer.raiseError(error, `${stage} of ${layer}`);
1812
+ }
1813
+ // TODO - mark layers with exceptions as bad and remove from rendering cycle?
1814
+ /** Match all layers, checking for caught errors
1815
+ to avoid having an exception in one layer disrupt other layers */
1816
+ _updateLayers(oldLayers, newLayers) {
1817
+ const oldLayerMap = {};
1818
+ for (const oldLayer of oldLayers) {
1819
+ if (oldLayerMap[oldLayer.id]) {
1820
+ log_default.warn(`Multiple old layers with same id ${oldLayer.id}`)();
1821
+ } else {
1822
+ oldLayerMap[oldLayer.id] = oldLayer;
1823
+ }
1824
+ }
1825
+ if (this._defaultShaderModulesChanged) {
1826
+ for (const layer of oldLayers) {
1827
+ layer.setNeedsUpdate();
1828
+ layer.setChangeFlags({ extensionsChanged: true });
1829
+ }
1830
+ this._defaultShaderModulesChanged = false;
1831
+ }
1832
+ const generatedLayers = [];
1833
+ this._updateSublayersRecursively(newLayers, oldLayerMap, generatedLayers);
1834
+ this._finalizeOldLayers(oldLayerMap);
1835
+ let needsUpdate = false;
1836
+ for (const layer of generatedLayers) {
1837
+ if (layer.hasUniformTransition()) {
1838
+ needsUpdate = `Uniform transition in ${layer}`;
1839
+ break;
1840
+ }
1841
+ }
1842
+ this._needsUpdate = needsUpdate;
1843
+ this.layers = generatedLayers;
1844
+ }
1845
+ /* eslint-disable complexity,max-statements */
1846
+ // Note: adds generated layers to `generatedLayers` array parameter
1847
+ _updateSublayersRecursively(newLayers, oldLayerMap, generatedLayers) {
1848
+ for (const newLayer of newLayers) {
1849
+ newLayer.context = this.context;
1850
+ const oldLayer = oldLayerMap[newLayer.id];
1851
+ if (oldLayer === null) {
1852
+ log_default.warn(`Multiple new layers with same id ${newLayer.id}`)();
1853
+ }
1854
+ oldLayerMap[newLayer.id] = null;
1855
+ let sublayers = null;
1856
+ try {
1857
+ if (this._debug && oldLayer !== newLayer) {
1858
+ newLayer.validateProps();
1859
+ }
1860
+ if (!oldLayer) {
1861
+ this._initializeLayer(newLayer);
1862
+ } else {
1863
+ this._transferLayerState(oldLayer, newLayer);
1864
+ this._updateLayer(newLayer);
1865
+ }
1866
+ generatedLayers.push(newLayer);
1867
+ sublayers = newLayer.isComposite ? newLayer.getSubLayers() : null;
1868
+ } catch (err) {
1869
+ this._handleError("matching", err, newLayer);
1870
+ }
1871
+ if (sublayers) {
1872
+ this._updateSublayersRecursively(sublayers, oldLayerMap, generatedLayers);
1873
+ }
1874
+ }
1875
+ }
1876
+ /* eslint-enable complexity,max-statements */
1877
+ // Finalize any old layers that were not matched
1878
+ _finalizeOldLayers(oldLayerMap) {
1879
+ for (const layerId in oldLayerMap) {
1880
+ const layer = oldLayerMap[layerId];
1881
+ if (layer) {
1882
+ this._finalizeLayer(layer);
1883
+ }
1884
+ }
1885
+ }
1886
+ // / EXCEPTION SAFE LAYER ACCESS
1887
+ /** Safely initializes a single layer, calling layer methods */
1888
+ _initializeLayer(layer) {
1889
+ try {
1890
+ layer._initialize();
1891
+ layer.lifecycle = LIFECYCLE.INITIALIZED;
1892
+ } catch (err) {
1893
+ this._handleError("initialization", err, layer);
1894
+ }
1895
+ }
1896
+ /** Transfer state from one layer to a newer version */
1897
+ _transferLayerState(oldLayer, newLayer) {
1898
+ newLayer._transferState(oldLayer);
1899
+ newLayer.lifecycle = LIFECYCLE.MATCHED;
1900
+ if (newLayer !== oldLayer) {
1901
+ oldLayer.lifecycle = LIFECYCLE.AWAITING_GC;
1902
+ }
1903
+ }
1904
+ /** Safely updates a single layer, cleaning all flags */
1905
+ _updateLayer(layer) {
1906
+ try {
1907
+ layer._update();
1908
+ } catch (err) {
1909
+ this._handleError("update", err, layer);
1910
+ }
1911
+ }
1912
+ /** Safely finalizes a single layer, removing all resources */
1913
+ _finalizeLayer(layer) {
1914
+ this._needsRedraw = this._needsRedraw || `finalized ${layer}`;
1915
+ layer.lifecycle = LIFECYCLE.AWAITING_FINALIZATION;
1916
+ try {
1917
+ layer._finalize();
1918
+ layer.lifecycle = LIFECYCLE.FINALIZED;
1919
+ } catch (err) {
1920
+ this._handleError("finalization", err, layer);
1921
+ }
1922
+ }
1923
+ };
1924
+
1925
+ // ../core/src/utils/deep-equal.ts
1926
+ function deepEqual(a, b, depth) {
1927
+ if (a === b) {
1928
+ return true;
1929
+ }
1930
+ if (!depth || !a || !b) {
1931
+ return false;
1932
+ }
1933
+ if (Array.isArray(a)) {
1934
+ if (!Array.isArray(b) || a.length !== b.length) {
1935
+ return false;
1936
+ }
1937
+ for (let i = 0; i < a.length; i++) {
1938
+ if (!deepEqual(a[i], b[i], depth - 1)) {
1939
+ return false;
1940
+ }
1941
+ }
1942
+ return true;
1943
+ }
1944
+ if (Array.isArray(b)) {
1945
+ return false;
1946
+ }
1947
+ if (typeof a === "object" && typeof b === "object") {
1948
+ const aKeys = Object.keys(a);
1949
+ const bKeys = Object.keys(b);
1950
+ if (aKeys.length !== bKeys.length) {
1951
+ return false;
1952
+ }
1953
+ for (const key of aKeys) {
1954
+ if (!b.hasOwnProperty(key)) {
1955
+ return false;
1956
+ }
1957
+ if (!deepEqual(a[key], b[key], depth - 1)) {
1958
+ return false;
1959
+ }
1960
+ }
1961
+ return true;
1962
+ }
1963
+ return false;
1964
+ }
1965
+
1966
+ // ../core/src/utils/positions.ts
1967
+ var NUMBER_REGEX = /^(?:\d+\.?\d*|\.\d+)$/;
1968
+ function parsePosition(value) {
1969
+ switch (typeof value) {
1970
+ case "number":
1971
+ if (!Number.isFinite(value)) {
1972
+ throw new Error(`Could not parse position string ${value}`);
1973
+ }
1974
+ return { type: "literal", value };
1975
+ case "string":
1976
+ try {
1977
+ const tokens = tokenize(value);
1978
+ const parser = new LayoutExpressionParser(tokens);
1979
+ return parser.parseExpression();
1980
+ } catch (error) {
1981
+ const reason = error instanceof Error ? error.message : String(error);
1982
+ throw new Error(`Could not parse position string ${value}: ${reason}`);
1983
+ }
1984
+ default:
1985
+ throw new Error(`Could not parse position string ${value}`);
1986
+ }
1987
+ }
1988
+ function evaluateLayoutExpression(expression, extent) {
1989
+ switch (expression.type) {
1990
+ case "literal":
1991
+ return expression.value;
1992
+ case "percentage":
1993
+ return Math.round(expression.value * extent);
1994
+ case "binary":
1995
+ const left = evaluateLayoutExpression(expression.left, extent);
1996
+ const right = evaluateLayoutExpression(expression.right, extent);
1997
+ return expression.operator === "+" ? left + right : left - right;
1998
+ default:
1999
+ throw new Error("Unknown layout expression type");
2000
+ }
2001
+ }
2002
+ function getPosition(expression, extent) {
2003
+ return evaluateLayoutExpression(expression, extent);
2004
+ }
2005
+ function tokenize(input) {
2006
+ const tokens = [];
2007
+ let index = 0;
2008
+ while (index < input.length) {
2009
+ const char = input[index];
2010
+ if (/\s/.test(char)) {
2011
+ index++;
2012
+ continue;
2013
+ }
2014
+ if (char === "+" || char === "-" || char === "(" || char === ")" || char === "%") {
2015
+ tokens.push({ type: "symbol", value: char });
2016
+ index++;
2017
+ continue;
2018
+ }
2019
+ if (isDigit(char) || char === ".") {
2020
+ const start = index;
2021
+ let hasDecimal = char === ".";
2022
+ index++;
2023
+ while (index < input.length) {
2024
+ const next = input[index];
2025
+ if (isDigit(next)) {
2026
+ index++;
2027
+ continue;
2028
+ }
2029
+ if (next === "." && !hasDecimal) {
2030
+ hasDecimal = true;
2031
+ index++;
2032
+ continue;
2033
+ }
2034
+ break;
2035
+ }
2036
+ const numberString = input.slice(start, index);
2037
+ if (!NUMBER_REGEX.test(numberString)) {
2038
+ throw new Error("Invalid number token");
2039
+ }
2040
+ tokens.push({ type: "number", value: parseFloat(numberString) });
2041
+ continue;
2042
+ }
2043
+ if (isAlpha(char)) {
2044
+ const start = index;
2045
+ while (index < input.length && isAlpha(input[index])) {
2046
+ index++;
2047
+ }
2048
+ const word = input.slice(start, index).toLowerCase();
2049
+ tokens.push({ type: "word", value: word });
2050
+ continue;
2051
+ }
2052
+ throw new Error("Invalid token in position string");
2053
+ }
2054
+ return tokens;
2055
+ }
2056
+ var LayoutExpressionParser = class {
2057
+ constructor(tokens) {
2058
+ this.index = 0;
2059
+ this.tokens = tokens;
2060
+ }
2061
+ parseExpression() {
2062
+ const expression = this.parseBinaryExpression();
2063
+ if (this.index < this.tokens.length) {
2064
+ throw new Error("Unexpected token at end of expression");
2065
+ }
2066
+ return expression;
2067
+ }
2068
+ parseBinaryExpression() {
2069
+ let expression = this.parseFactor();
2070
+ let token = this.peek();
2071
+ while (isAddSubSymbol(token)) {
2072
+ this.index++;
2073
+ const right = this.parseFactor();
2074
+ expression = { type: "binary", operator: token.value, left: expression, right };
2075
+ token = this.peek();
2076
+ }
2077
+ return expression;
2078
+ }
2079
+ parseFactor() {
2080
+ const token = this.peek();
2081
+ if (!token) {
2082
+ throw new Error("Unexpected end of expression");
2083
+ }
2084
+ if (token.type === "symbol" && token.value === "+") {
2085
+ this.index++;
2086
+ return this.parseFactor();
2087
+ }
2088
+ if (token.type === "symbol" && token.value === "-") {
2089
+ this.index++;
2090
+ const factor = this.parseFactor();
2091
+ return { type: "binary", operator: "-", left: { type: "literal", value: 0 }, right: factor };
2092
+ }
2093
+ if (token.type === "symbol" && token.value === "(") {
2094
+ this.index++;
2095
+ const expression = this.parseBinaryExpression();
2096
+ if (!this.consumeSymbol(")")) {
2097
+ throw new Error("Missing closing parenthesis");
2098
+ }
2099
+ return expression;
2100
+ }
2101
+ if (token.type === "word" && token.value === "calc") {
2102
+ this.index++;
2103
+ if (!this.consumeSymbol("(")) {
2104
+ throw new Error("Missing opening parenthesis after calc");
2105
+ }
2106
+ const expression = this.parseBinaryExpression();
2107
+ if (!this.consumeSymbol(")")) {
2108
+ throw new Error("Missing closing parenthesis");
2109
+ }
2110
+ return expression;
2111
+ }
2112
+ if (token.type === "number") {
2113
+ this.index++;
2114
+ const numberValue = token.value;
2115
+ const nextToken = this.peek();
2116
+ if (nextToken && nextToken.type === "symbol" && nextToken.value === "%") {
2117
+ this.index++;
2118
+ return { type: "percentage", value: numberValue / 100 };
2119
+ }
2120
+ if (nextToken && nextToken.type === "word" && nextToken.value === "px") {
2121
+ this.index++;
2122
+ return { type: "literal", value: numberValue };
2123
+ }
2124
+ return { type: "literal", value: numberValue };
2125
+ }
2126
+ throw new Error("Unexpected token in expression");
2127
+ }
2128
+ consumeSymbol(value) {
2129
+ const token = this.peek();
2130
+ if (token && token.type === "symbol" && token.value === value) {
2131
+ this.index++;
2132
+ return true;
2133
+ }
2134
+ return false;
2135
+ }
2136
+ peek() {
2137
+ return this.tokens[this.index] || null;
2138
+ }
2139
+ };
2140
+ function isDigit(char) {
2141
+ return char >= "0" && char <= "9";
2142
+ }
2143
+ function isAlpha(char) {
2144
+ return char >= "a" && char <= "z" || char >= "A" && char <= "Z";
2145
+ }
2146
+ function isAddSubSymbol(token) {
2147
+ return Boolean(token && token.type === "symbol" && (token.value === "+" || token.value === "-"));
2148
+ }
2149
+
2150
+ // ../core/src/utils/deep-merge.ts
2151
+ function deepMergeViewState(a, b) {
2152
+ const result = { ...a };
2153
+ for (const key in b) {
2154
+ if (key === "id") continue;
2155
+ if (Array.isArray(result[key]) && Array.isArray(b[key])) {
2156
+ result[key] = mergeNumericArray(result[key], b[key]);
2157
+ } else {
2158
+ result[key] = b[key];
2159
+ }
2160
+ }
2161
+ return result;
2162
+ }
2163
+ function mergeNumericArray(target, source2) {
2164
+ target = target.slice();
2165
+ for (let i = 0; i < source2.length; i++) {
2166
+ const v = source2[i];
2167
+ if (Number.isFinite(v)) {
2168
+ target[i] = v;
2169
+ }
2170
+ }
2171
+ return target;
2172
+ }
2173
+
2174
+ // ../core/src/views/view.ts
2175
+ var View = class {
2176
+ constructor(props) {
2177
+ const { id, x = 0, y = 0, width = "100%", height = "100%", padding = null } = props;
2178
+ this.id = id || this.constructor.displayName || "view";
2179
+ this.props = { ...props, id: this.id };
2180
+ this._x = parsePosition(x);
2181
+ this._y = parsePosition(y);
2182
+ this._width = parsePosition(width);
2183
+ this._height = parsePosition(height);
2184
+ this._padding = padding && {
2185
+ left: parsePosition(padding.left || 0),
2186
+ right: parsePosition(padding.right || 0),
2187
+ top: parsePosition(padding.top || 0),
2188
+ bottom: parsePosition(padding.bottom || 0)
2189
+ };
2190
+ this.equals = this.equals.bind(this);
2191
+ Object.seal(this);
2192
+ }
2193
+ equals(view) {
2194
+ if (this === view) {
2195
+ return true;
2196
+ }
2197
+ return this.constructor === view.constructor && deepEqual(this.props, view.props, 2);
2198
+ }
2199
+ /** Clone this view with modified props */
2200
+ clone(newProps) {
2201
+ const ViewConstructor = this.constructor;
2202
+ return new ViewConstructor({ ...this.props, ...newProps });
2203
+ }
2204
+ /** Make viewport from canvas dimensions and view state */
2205
+ makeViewport({ width, height, viewState }) {
2206
+ viewState = this.filterViewState(viewState);
2207
+ const viewportDimensions = this.getDimensions({ width, height });
2208
+ if (!viewportDimensions.height || !viewportDimensions.width) {
2209
+ return null;
2210
+ }
2211
+ const ViewportType = this.getViewportType(viewState);
2212
+ return new ViewportType({ ...viewState, ...this.props, ...viewportDimensions });
2213
+ }
2214
+ getViewStateId() {
2215
+ const { viewState } = this.props;
2216
+ if (typeof viewState === "string") {
2217
+ return viewState;
2218
+ }
2219
+ return (viewState == null ? void 0 : viewState.id) || this.id;
2220
+ }
2221
+ // Allows view to override (or completely define) viewState
2222
+ filterViewState(viewState) {
2223
+ if (this.props.viewState && typeof this.props.viewState === "object") {
2224
+ if (!this.props.viewState.id) {
2225
+ return this.props.viewState;
2226
+ }
2227
+ return deepMergeViewState(viewState, this.props.viewState);
2228
+ }
2229
+ return viewState;
2230
+ }
2231
+ /** Resolve the dimensions of the view from overall canvas dimensions */
2232
+ getDimensions({ width, height }) {
2233
+ const dimensions = {
2234
+ x: getPosition(this._x, width),
2235
+ y: getPosition(this._y, height),
2236
+ width: getPosition(this._width, width),
2237
+ height: getPosition(this._height, height)
2238
+ };
2239
+ if (this._padding) {
2240
+ dimensions.padding = {
2241
+ left: getPosition(this._padding.left, width),
2242
+ top: getPosition(this._padding.top, height),
2243
+ right: getPosition(this._padding.right, width),
2244
+ bottom: getPosition(this._padding.bottom, height)
2245
+ };
2246
+ }
2247
+ return dimensions;
2248
+ }
2249
+ // Used by sub classes to resolve controller props
2250
+ get controller() {
2251
+ const opts = this.props.controller;
2252
+ if (!opts) {
2253
+ return null;
2254
+ }
2255
+ if (opts === true) {
2256
+ return { type: this.ControllerType };
2257
+ }
2258
+ if (typeof opts === "function") {
2259
+ return { type: opts };
2260
+ }
2261
+ return { type: this.ControllerType, ...opts };
2262
+ }
2263
+ };
2264
+
2265
+ // ../core/src/controllers/map-controller.ts
2266
+ var import_core7 = require("@math.gl/core");
2267
+
2268
+ // ../core/src/transitions/transition.ts
2269
+ var Transition = class {
2270
+ /**
2271
+ * @params timeline {Timeline}
2272
+ */
2273
+ constructor(timeline) {
2274
+ this._inProgress = false;
2275
+ this._handle = null;
2276
+ this.time = 0;
2277
+ // @ts-expect-error
2278
+ this.settings = {
2279
+ duration: 0
2280
+ };
2281
+ this._timeline = timeline;
2282
+ }
2283
+ /* Public API */
2284
+ get inProgress() {
2285
+ return this._inProgress;
2286
+ }
2287
+ /**
2288
+ * (re)start this transition.
2289
+ * @params props {object} - optional overriding props. see constructor
2290
+ */
2291
+ start(settings) {
2292
+ var _a2, _b;
2293
+ this.cancel();
2294
+ this.settings = settings;
2295
+ this._inProgress = true;
2296
+ (_b = (_a2 = this.settings).onStart) == null ? void 0 : _b.call(_a2, this);
2297
+ }
2298
+ /**
2299
+ * end this transition if it is in progress.
2300
+ */
2301
+ end() {
2302
+ var _a2, _b;
2303
+ if (this._inProgress) {
2304
+ this._timeline.removeChannel(this._handle);
2305
+ this._handle = null;
2306
+ this._inProgress = false;
2307
+ (_b = (_a2 = this.settings).onEnd) == null ? void 0 : _b.call(_a2, this);
2308
+ }
2309
+ }
2310
+ /**
2311
+ * cancel this transition if it is in progress.
2312
+ */
2313
+ cancel() {
2314
+ var _a2, _b;
2315
+ if (this._inProgress) {
2316
+ (_b = (_a2 = this.settings).onInterrupt) == null ? void 0 : _b.call(_a2, this);
2317
+ this._timeline.removeChannel(this._handle);
2318
+ this._handle = null;
2319
+ this._inProgress = false;
2320
+ }
2321
+ }
2322
+ /**
2323
+ * update this transition. Returns `true` if updated.
2324
+ */
2325
+ update() {
2326
+ var _a2, _b;
2327
+ if (!this._inProgress) {
2328
+ return false;
2329
+ }
2330
+ if (this._handle === null) {
2331
+ const { _timeline: timeline, settings } = this;
2332
+ this._handle = timeline.addChannel({
2333
+ delay: timeline.getTime(),
2334
+ duration: settings.duration
2335
+ });
2336
+ }
2337
+ this.time = this._timeline.getTime(this._handle);
2338
+ this._onUpdate();
2339
+ (_b = (_a2 = this.settings).onUpdate) == null ? void 0 : _b.call(_a2, this);
2340
+ if (this._timeline.isFinished(this._handle)) {
2341
+ this.end();
2342
+ }
2343
+ return true;
2344
+ }
2345
+ /* Private API */
2346
+ _onUpdate() {
2347
+ }
2348
+ };
2349
+
2350
+ // ../core/src/controllers/transition-manager.ts
2351
+ var noop = () => {
2352
+ };
2353
+ var PRESERVE_CONSTRAINT_CONTEXT = { mode: "preserve" };
2354
+ var HARD_CONSTRAINT_CONTEXT = { mode: "hard" };
2355
+ var TRANSITION_EVENTS = {
2356
+ BREAK: 1,
2357
+ SNAP_TO_END: 2,
2358
+ IGNORE: 3
2359
+ };
2360
+ var DEFAULT_EASING = (t) => t;
2361
+ var DEFAULT_INTERRUPTION = TRANSITION_EVENTS.BREAK;
2362
+ var TransitionManager = class {
2363
+ constructor(opts) {
2364
+ this._onTransitionUpdate = (transition) => {
2365
+ const {
2366
+ time,
2367
+ settings: { interpolator, startProps, endProps, duration, easing }
2368
+ } = transition;
2369
+ const t = easing(time / duration);
2370
+ const viewport = interpolator.interpolateProps(startProps, endProps, t);
2371
+ this.propsInTransition = this.getControllerState(
2372
+ {
2373
+ ...this.props,
2374
+ ...viewport
2375
+ },
2376
+ PRESERVE_CONSTRAINT_CONTEXT
2377
+ ).getViewportProps();
2378
+ this.onViewStateChange({
2379
+ viewState: this.propsInTransition,
2380
+ oldViewState: this.props
2381
+ });
2382
+ };
2383
+ this.getControllerState = opts.getControllerState;
2384
+ this.propsInTransition = null;
2385
+ this.transition = new Transition(opts.timeline);
2386
+ this.onViewStateChange = opts.onViewStateChange || noop;
2387
+ this.onStateChange = opts.onStateChange || noop;
2388
+ }
2389
+ finalize() {
2390
+ this.transition.cancel();
2391
+ }
2392
+ // Returns current transitioned viewport.
2393
+ getViewportInTransition() {
2394
+ return this.propsInTransition;
2395
+ }
2396
+ // Process the vewiport change, either ignore or trigger a new transition.
2397
+ // Return true if a new transition is triggered, false otherwise.
2398
+ processViewStateChange(nextProps) {
2399
+ let transitionTriggered = false;
2400
+ const currentProps = this.props;
2401
+ this.props = nextProps;
2402
+ if (!currentProps || this._shouldIgnoreViewportChange(currentProps, nextProps)) {
2403
+ return false;
2404
+ }
2405
+ if (this._isTransitionEnabled(nextProps)) {
2406
+ let startProps = currentProps;
2407
+ if (this.transition.inProgress) {
2408
+ const { interruption, endProps } = this.transition.settings;
2409
+ startProps = {
2410
+ ...currentProps,
2411
+ ...interruption === TRANSITION_EVENTS.SNAP_TO_END ? endProps : this.propsInTransition || currentProps
2412
+ };
2413
+ }
2414
+ this._triggerTransition(startProps, nextProps);
2415
+ transitionTriggered = true;
2416
+ } else {
2417
+ this.transition.cancel();
2418
+ }
2419
+ return transitionTriggered;
2420
+ }
2421
+ updateTransition() {
2422
+ this.transition.update();
2423
+ }
2424
+ // Helper methods
2425
+ _isTransitionEnabled(props) {
2426
+ const { transitionDuration, transitionInterpolator } = props;
2427
+ return (transitionDuration > 0 || transitionDuration === "auto") && Boolean(transitionInterpolator);
2428
+ }
2429
+ _isUpdateDueToCurrentTransition(props) {
2430
+ if (this.transition.inProgress && this.propsInTransition) {
2431
+ return this.transition.settings.interpolator.arePropsEqual(
2432
+ props,
2433
+ this.propsInTransition
2434
+ );
2435
+ }
2436
+ return false;
2437
+ }
2438
+ _shouldIgnoreViewportChange(currentProps, nextProps) {
2439
+ if (this.transition.inProgress) {
2440
+ const transitionSettings = this.transition.settings;
2441
+ return transitionSettings.interruption === TRANSITION_EVENTS.IGNORE || // Ignore update if it is due to current active transition.
2442
+ this._isUpdateDueToCurrentTransition(nextProps);
2443
+ }
2444
+ if (this._isTransitionEnabled(nextProps)) {
2445
+ return nextProps.transitionInterpolator.arePropsEqual(
2446
+ currentProps,
2447
+ nextProps
2448
+ );
2449
+ }
2450
+ return true;
2451
+ }
2452
+ _triggerTransition(startProps, endProps) {
2453
+ const startViewstate = this.getControllerState(startProps, PRESERVE_CONSTRAINT_CONTEXT);
2454
+ const endViewStateProps = this.getControllerState(
2455
+ endProps,
2456
+ HARD_CONSTRAINT_CONTEXT
2457
+ ).shortestPathFrom(startViewstate);
2458
+ const transitionInterpolator = endProps.transitionInterpolator;
2459
+ const duration = transitionInterpolator.getDuration ? transitionInterpolator.getDuration(startProps, endProps) : endProps.transitionDuration;
2460
+ if (duration === 0) {
2461
+ return;
2462
+ }
2463
+ const initialProps = transitionInterpolator.initializeProps(startProps, endViewStateProps);
2464
+ this.propsInTransition = {};
2465
+ const transitionSettings = {
2466
+ duration,
2467
+ easing: endProps.transitionEasing || DEFAULT_EASING,
2468
+ interpolator: transitionInterpolator,
2469
+ interruption: endProps.transitionInterruption || DEFAULT_INTERRUPTION,
2470
+ startProps: initialProps.start,
2471
+ endProps: initialProps.end,
2472
+ onStart: endProps.onTransitionStart,
2473
+ onUpdate: this._onTransitionUpdate,
2474
+ onInterrupt: this._onTransitionEnd(endProps.onTransitionInterrupt),
2475
+ onEnd: this._onTransitionEnd(endProps.onTransitionEnd)
2476
+ };
2477
+ this.transition.start(transitionSettings);
2478
+ this.onStateChange({ inTransition: true });
2479
+ this.updateTransition();
2480
+ }
2481
+ _onTransitionEnd(callback) {
2482
+ return (transition) => {
2483
+ this.propsInTransition = null;
2484
+ this.onStateChange({
2485
+ inTransition: false,
2486
+ isZooming: false,
2487
+ isPanning: false,
2488
+ isRotating: false
2489
+ });
2490
+ callback == null ? void 0 : callback(transition);
2491
+ };
2492
+ }
2493
+ };
2494
+
2495
+ // ../core/src/transitions/transition-interpolator.ts
2496
+ var import_core5 = require("@math.gl/core");
2497
+
2498
+ // ../core/src/utils/assert.ts
2499
+ function assert(condition, message) {
2500
+ if (!condition) {
2501
+ throw new Error(message || "deck.gl: assertion failed.");
2502
+ }
2503
+ }
2504
+
2505
+ // ../core/src/transitions/transition-interpolator.ts
2506
+ var TransitionInterpolator = class {
2507
+ /**
2508
+ * @param opts {array|object}
2509
+ * @param opts.compare {array} - prop names used in equality check
2510
+ * @param opts.extract {array} - prop names needed for interpolation
2511
+ * @param opts.required {array} - prop names that must be supplied
2512
+ * alternatively, supply one list of prop names as `opts` if all of the above are the same.
2513
+ */
2514
+ constructor(opts) {
2515
+ const { compare, extract, required } = opts;
2516
+ this._propsToCompare = compare;
2517
+ this._propsToExtract = extract || compare;
2518
+ this._requiredProps = required;
2519
+ }
2520
+ /**
2521
+ * Checks if two sets of props need transition in between
2522
+ * @param currentProps {object} - a list of viewport props
2523
+ * @param nextProps {object} - a list of viewport props
2524
+ * @returns {bool} - true if two props are equivalent
2525
+ */
2526
+ arePropsEqual(currentProps, nextProps) {
2527
+ for (const key of this._propsToCompare) {
2528
+ if (!(key in currentProps) || !(key in nextProps) || !(0, import_core5.equals)(currentProps[key], nextProps[key])) {
2529
+ return false;
2530
+ }
2531
+ }
2532
+ return true;
2533
+ }
2534
+ /**
2535
+ * Called before transition starts to validate/pre-process start and end props
2536
+ * @param startProps {object} - a list of starting viewport props
2537
+ * @param endProps {object} - a list of target viewport props
2538
+ * @returns {Object} {start, end} - start and end props to be passed
2539
+ * to `interpolateProps`
2540
+ */
2541
+ initializeProps(startProps, endProps) {
2542
+ const startViewStateProps = {};
2543
+ const endViewStateProps = {};
2544
+ for (const key of this._propsToExtract) {
2545
+ if (key in startProps || key in endProps) {
2546
+ startViewStateProps[key] = startProps[key];
2547
+ endViewStateProps[key] = endProps[key];
2548
+ }
2549
+ }
2550
+ this._checkRequiredProps(startViewStateProps);
2551
+ this._checkRequiredProps(endViewStateProps);
2552
+ return { start: startViewStateProps, end: endViewStateProps };
2553
+ }
2554
+ /**
2555
+ * Returns transition duration
2556
+ * @param startProps {object} - a list of starting viewport props
2557
+ * @param endProps {object} - a list of target viewport props
2558
+ * @returns {Number} - transition duration in milliseconds
2559
+ */
2560
+ getDuration(startProps, endProps) {
2561
+ return endProps.transitionDuration;
2562
+ }
2563
+ _checkRequiredProps(props) {
2564
+ if (!this._requiredProps) {
2565
+ return;
2566
+ }
2567
+ this._requiredProps.forEach((propName) => {
2568
+ const value = props[propName];
2569
+ assert(
2570
+ Number.isFinite(value) || Array.isArray(value),
2571
+ `${propName} is required for transition`
2572
+ );
2573
+ });
2574
+ }
2575
+ };
2576
+
2577
+ // ../core/src/transitions/linear-interpolator.ts
2578
+ var import_core6 = require("@math.gl/core");
2579
+ var DEFAULT_PROPS = ["longitude", "latitude", "zoom", "bearing", "pitch"];
2580
+ var DEFAULT_REQUIRED_PROPS = ["longitude", "latitude", "zoom"];
2581
+ var LinearInterpolator = class extends TransitionInterpolator {
2582
+ /**
2583
+ * @param {Object} opts
2584
+ * @param {Array} opts.transitionProps - list of props to apply linear transition to.
2585
+ * @param {Array} opts.around - a screen point to zoom/rotate around.
2586
+ * @param {Function} opts.makeViewport - construct a viewport instance with given props.
2587
+ */
2588
+ constructor(opts = {}) {
2589
+ const transitionProps = Array.isArray(opts) ? opts : opts.transitionProps;
2590
+ const normalizedOpts = Array.isArray(opts) ? {} : opts;
2591
+ normalizedOpts.transitionProps = Array.isArray(transitionProps) ? {
2592
+ compare: transitionProps,
2593
+ required: transitionProps
2594
+ } : transitionProps || {
2595
+ compare: DEFAULT_PROPS,
2596
+ required: DEFAULT_REQUIRED_PROPS
2597
+ };
2598
+ super(normalizedOpts.transitionProps);
2599
+ this.opts = normalizedOpts;
2600
+ }
2601
+ initializeProps(startProps, endProps) {
2602
+ const result = super.initializeProps(startProps, endProps);
2603
+ const { makeViewport, around } = this.opts;
2604
+ if (makeViewport && around) {
2605
+ const startViewport = makeViewport(startProps);
2606
+ const endViewport = makeViewport(endProps);
2607
+ const aroundPosition = startViewport.unproject(around);
2608
+ result.start.around = around;
2609
+ Object.assign(result.end, {
2610
+ around: endViewport.project(aroundPosition),
2611
+ aroundPosition,
2612
+ width: endProps.width,
2613
+ height: endProps.height
2614
+ });
2615
+ }
2616
+ return result;
2617
+ }
2618
+ interpolateProps(startProps, endProps, t) {
2619
+ const propsInTransition = {};
2620
+ for (const key of this._propsToExtract) {
2621
+ propsInTransition[key] = (0, import_core6.lerp)(startProps[key] || 0, endProps[key] || 0, t);
2622
+ }
2623
+ if (endProps.aroundPosition && this.opts.makeViewport) {
2624
+ const viewport = this.opts.makeViewport({ ...endProps, ...propsInTransition });
2625
+ Object.assign(
2626
+ propsInTransition,
2627
+ viewport.panByPosition(
2628
+ endProps.aroundPosition,
2629
+ // anchor point in current screen coordinates
2630
+ (0, import_core6.lerp)(startProps.around, endProps.around, t)
2631
+ )
2632
+ );
2633
+ }
2634
+ return propsInTransition;
2635
+ }
2636
+ };
2637
+
2638
+ // ../core/src/controllers/controller.ts
2639
+ var import_web_mercator3 = require("@math.gl/web-mercator");
2640
+ var NO_TRANSITION_PROPS = {
2641
+ transitionDuration: 0
2642
+ };
2643
+ var DEFAULT_INERTIA = 300;
2644
+ var REBOUND_DURATION = 300;
2645
+ var INERTIA_EASING = (t) => 1 - (1 - t) * (1 - t);
2646
+ var EASE_OUT_EXPONENTIAL = (t) => t === 1 ? 1 : 1 - Math.pow(2, -10 * t);
2647
+ var EVENT_TYPES = {
2648
+ WHEEL: ["wheel"],
2649
+ PAN: ["panstart", "panmove", "panend"],
2650
+ PINCH: ["pinchstart", "pinchmove", "pinchend"],
2651
+ MULTI_PAN: ["multipanstart", "multipanmove", "multipanend"],
2652
+ DOUBLE_CLICK: ["dblclick"],
2653
+ DOUBLE_CLICK_DRAG: [
2654
+ "dblclickdragstart",
2655
+ "dblclickdragmove",
2656
+ "dblclickdragend",
2657
+ "dblclickdragcancel"
2658
+ ],
2659
+ KEYBOARD: ["keydown"]
2660
+ };
2661
+ var pinchEventWorkaround = {};
2662
+ var Controller = class {
2663
+ constructor(opts) {
2664
+ this.state = {};
2665
+ this._events = {};
2666
+ this._interactionState = {
2667
+ isDragging: false
2668
+ };
2669
+ this._customEvents = [];
2670
+ this._eventStartBlocked = null;
2671
+ this._panMove = false;
2672
+ this._multiPanMode = null;
2673
+ this._multiPanStartCenter = null;
2674
+ this._doubleClickDragAnchor = null;
2675
+ this._suppressDoubleClickUntil = 0;
2676
+ this.invertPan = false;
2677
+ this.dragMode = "rotate";
2678
+ this.inertia = 0;
2679
+ this.scrollZoom = true;
2680
+ this.dragPan = true;
2681
+ this.dragRotate = true;
2682
+ this.doubleClickZoom = true;
2683
+ this.doubleClickDragZoom = true;
2684
+ this.touchZoom = true;
2685
+ this.touchRotate = false;
2686
+ this.multiTouchDrag = null;
2687
+ this.trackpadGesture = false;
2688
+ this.zoomAround = "pointer";
2689
+ this.keyboard = true;
2690
+ this.transitionManager = new TransitionManager({
2691
+ ...opts,
2692
+ getControllerState: (props, constraintContext) => new this.ControllerState({
2693
+ ...props,
2694
+ constraintContext,
2695
+ makeViewport: opts.makeViewport
2696
+ }),
2697
+ onViewStateChange: this._onTransition.bind(this),
2698
+ onStateChange: this._setInteractionState.bind(this)
2699
+ });
2700
+ this.handleEvent = this.handleEvent.bind(this);
2701
+ this.eventManager = opts.eventManager;
2702
+ this.onViewStateChange = opts.onViewStateChange || (() => {
2703
+ });
2704
+ this.onStateChange = opts.onStateChange || (() => {
2705
+ });
2706
+ this.makeViewport = opts.makeViewport;
2707
+ this.pickPosition = opts.pickPosition;
2708
+ }
2709
+ set events(customEvents) {
2710
+ this.toggleEvents(this._customEvents, false);
2711
+ this.toggleEvents(customEvents, true);
2712
+ this._customEvents = customEvents;
2713
+ if (this.props) {
2714
+ this.setProps(this.props);
2715
+ }
2716
+ }
2717
+ finalize() {
2718
+ var _a2;
2719
+ for (const eventName in this._events) {
2720
+ if (this._events[eventName]) {
2721
+ (_a2 = this.eventManager) == null ? void 0 : _a2.off(eventName, this.handleEvent);
2722
+ }
2723
+ }
2724
+ this.transitionManager.finalize();
2725
+ }
2726
+ /**
2727
+ * Callback for events
2728
+ */
2729
+ handleEvent(event) {
2730
+ this._controllerState = void 0;
2731
+ const eventStartBlocked = this._eventStartBlocked;
2732
+ switch (event.type) {
2733
+ case "panstart":
2734
+ return eventStartBlocked ? false : this._onPanStart(event);
2735
+ case "panmove":
2736
+ return this._onPan(event);
2737
+ case "panend":
2738
+ return this._onPanEnd(event);
2739
+ case "pinchstart":
2740
+ return eventStartBlocked || !this._isTrackpadGestureAllowed(event) ? false : this._onPinchStart(event);
2741
+ case "pinchmove":
2742
+ return this._isTrackpadGestureAllowed(event) ? this._onPinch(event) : false;
2743
+ case "pinchend":
2744
+ return this._isTrackpadGestureAllowed(event) ? this._onPinchEnd(event) : false;
2745
+ case "multipanstart":
2746
+ return eventStartBlocked ? false : this._onMultiPanStart(event);
2747
+ case "multipanmove":
2748
+ return this._onMultiPan(event);
2749
+ case "multipanend":
2750
+ return this._onMultiPanEnd(event);
2751
+ case "dblclick":
2752
+ return this._onDoubleClick(event);
2753
+ case "dblclickdragstart":
2754
+ return eventStartBlocked ? false : this._onDoubleClickDragStart(event);
2755
+ case "dblclickdragmove":
2756
+ return this._onDoubleClickDrag(event);
2757
+ case "dblclickdragend":
2758
+ case "dblclickdragcancel":
2759
+ return this._onDoubleClickDragEnd(event);
2760
+ case "wheel":
2761
+ return this._onWheel(event);
2762
+ case "keydown":
2763
+ return this._onKeyDown(event);
2764
+ default:
2765
+ return false;
2766
+ }
2767
+ }
2768
+ /* Event utils */
2769
+ // Event object: http://hammerjs.github.io/api/#event-object
2770
+ get controllerState() {
2771
+ this._controllerState = this._controllerState || new this.ControllerState({
2772
+ makeViewport: this.makeViewport,
2773
+ ...this.props,
2774
+ ...this.state
2775
+ });
2776
+ return this._controllerState;
2777
+ }
2778
+ getCenter(event) {
2779
+ const { x, y } = this.props;
2780
+ const { offsetCenter } = event;
2781
+ return [offsetCenter.x - x, offsetCenter.y - y];
2782
+ }
2783
+ /** Resolves the zoom anchor for pointer-based controls. */
2784
+ getZoomPosition(position) {
2785
+ if (this.zoomAround === "pointer") {
2786
+ return position;
2787
+ }
2788
+ const viewport = this.makeViewport(this.controllerState.getViewportProps());
2789
+ const [centerX, centerY] = (0, import_web_mercator3.worldToPixels)(viewport.center, viewport.pixelProjectionMatrix);
2790
+ return [centerX, centerY];
2791
+ }
2792
+ isPointInBounds(pos, event) {
2793
+ const { width, height } = this.props;
2794
+ if (event && event.handled) {
2795
+ return false;
2796
+ }
2797
+ const inside = pos[0] >= 0 && pos[0] <= width && pos[1] >= 0 && pos[1] <= height;
2798
+ if (inside && event) {
2799
+ event.stopPropagation();
2800
+ }
2801
+ return inside;
2802
+ }
2803
+ isFunctionKeyPressed(event) {
2804
+ const { srcEvent } = event;
2805
+ return Boolean(srcEvent.metaKey || srcEvent.altKey || srcEvent.ctrlKey || srcEvent.shiftKey);
2806
+ }
2807
+ isDragging() {
2808
+ return this._interactionState.isDragging || false;
2809
+ }
2810
+ // When a multi-touch event ends, e.g. pinch, not all pointers are lifted at the same time.
2811
+ // This triggers a brief `pan` event.
2812
+ // Calling this method will temporarily disable *start events to avoid conflicting transitions.
2813
+ blockEvents(timeout) {
2814
+ const timer = setTimeout(() => {
2815
+ if (this._eventStartBlocked === timer) {
2816
+ this._eventStartBlocked = null;
2817
+ }
2818
+ }, timeout);
2819
+ this._eventStartBlocked = timer;
2820
+ }
2821
+ /**
2822
+ * Extract interactivity options
2823
+ */
2824
+ setProps(props) {
2825
+ if (props.maxBoundsPadding === void 0) {
2826
+ props.maxBoundsPadding = null;
2827
+ }
2828
+ if (props.dragMode) {
2829
+ this.dragMode = props.dragMode;
2830
+ }
2831
+ const oldProps = this.props;
2832
+ this.props = props;
2833
+ if (!("transitionInterpolator" in props)) {
2834
+ props.transitionInterpolator = this._getTransitionProps().transitionInterpolator;
2835
+ }
2836
+ this.transitionManager.processViewStateChange(props);
2837
+ const { inertia } = props;
2838
+ this.inertia = Number.isFinite(inertia) ? inertia : inertia === true ? DEFAULT_INERTIA : 0;
2839
+ const {
2840
+ scrollZoom = true,
2841
+ dragPan = true,
2842
+ dragRotate = true,
2843
+ doubleClickZoom = true,
2844
+ doubleClickDragZoom = false,
2845
+ touchZoom = true,
2846
+ touchRotate = false,
2847
+ multiTouchDrag = touchRotate ? "rotate" : null,
2848
+ trackpadGesture = false,
2849
+ zoomAround = "pointer",
2850
+ keyboard = true
2851
+ } = props;
2852
+ const isInteractive = Boolean(this.onViewStateChange);
2853
+ this.toggleEvents(EVENT_TYPES.WHEEL, isInteractive && scrollZoom);
2854
+ this.toggleEvents(EVENT_TYPES.PAN, isInteractive);
2855
+ this.toggleEvents(
2856
+ EVENT_TYPES.PINCH,
2857
+ isInteractive && (touchZoom || multiTouchDrag === "rotate")
2858
+ );
2859
+ this.toggleEvents(EVENT_TYPES.MULTI_PAN, isInteractive && Boolean(multiTouchDrag));
2860
+ this.toggleEvents(EVENT_TYPES.DOUBLE_CLICK, isInteractive && doubleClickZoom);
2861
+ this.toggleEvents(EVENT_TYPES.DOUBLE_CLICK_DRAG, isInteractive && doubleClickDragZoom);
2862
+ this.toggleEvents(EVENT_TYPES.KEYBOARD, isInteractive && keyboard);
2863
+ this.scrollZoom = scrollZoom;
2864
+ this.dragPan = dragPan;
2865
+ this.dragRotate = dragRotate;
2866
+ this.doubleClickZoom = doubleClickZoom;
2867
+ this.doubleClickDragZoom = doubleClickDragZoom;
2868
+ this.touchZoom = touchZoom;
2869
+ this.touchRotate = multiTouchDrag === "rotate";
2870
+ this.multiTouchDrag = multiTouchDrag;
2871
+ this.trackpadGesture = trackpadGesture;
2872
+ this.zoomAround = zoomAround;
2873
+ this.keyboard = keyboard;
2874
+ const constraintChanged = !oldProps || oldProps.height !== props.height || oldProps.width !== props.width || oldProps.maxBounds !== props.maxBounds || oldProps.maxBoundsPadding !== props.maxBoundsPadding;
2875
+ if (constraintChanged && props.maxBounds) {
2876
+ const controllerState = new this.ControllerState({ ...props, makeViewport: this.makeViewport });
2877
+ const normalizedProps = controllerState.getViewportProps();
2878
+ const changed = Object.keys(normalizedProps).some(
2879
+ (key) => !deepEqual(normalizedProps[key], props[key], 1)
2880
+ );
2881
+ if (changed) {
2882
+ this.updateViewport(controllerState);
2883
+ }
2884
+ }
2885
+ }
2886
+ updateTransition() {
2887
+ this.transitionManager.updateTransition();
2888
+ }
2889
+ toggleEvents(eventNames, enabled) {
2890
+ if (this.eventManager) {
2891
+ eventNames.forEach((eventName) => {
2892
+ if (this._events[eventName] !== enabled) {
2893
+ this._events[eventName] = enabled;
2894
+ if (enabled) {
2895
+ this.eventManager.on(eventName, this.handleEvent);
2896
+ } else {
2897
+ this.eventManager.off(eventName, this.handleEvent);
2898
+ }
2899
+ }
2900
+ });
2901
+ }
2902
+ }
2903
+ // Private Methods
2904
+ /* Callback util */
2905
+ // formats map state and invokes callback function
2906
+ updateViewport(newControllerState, extraProps = null, interactionState = {}) {
2907
+ const viewState = { ...newControllerState.getViewportProps(), ...extraProps };
2908
+ const changed = this.controllerState !== newControllerState;
2909
+ this.state = newControllerState.getState();
2910
+ this._setInteractionState(interactionState);
2911
+ if (changed) {
2912
+ const oldViewState = this.controllerState && this.controllerState.getViewportProps();
2913
+ if (this.onViewStateChange) {
2914
+ this.onViewStateChange({
2915
+ viewState,
2916
+ interactionState: this._interactionState,
2917
+ oldViewState,
2918
+ viewId: this.props.id
2919
+ });
2920
+ }
2921
+ }
2922
+ }
2923
+ _onTransition(params) {
2924
+ this.onViewStateChange({
2925
+ ...params,
2926
+ interactionState: this._interactionState,
2927
+ viewId: this.props.id
2928
+ });
2929
+ }
2930
+ _setInteractionState(newStates) {
2931
+ Object.assign(this._interactionState, newStates);
2932
+ this.onStateChange(this._interactionState);
2933
+ }
2934
+ /** Maps a semantic input lifecycle to the constraint policy seen by controller state. */
2935
+ _getConstraintContext(_action, phase) {
2936
+ if (!this.props.rubberBand) {
2937
+ return { mode: "hard" };
2938
+ }
2939
+ return { mode: phase === "update" ? "elastic" : phase === "end" ? "rebound" : "hard" };
2940
+ }
2941
+ /** Returns a rebound transition when hard resolution changed the displayed viewport props. */
2942
+ _getReboundTransition(constraintContext, nextControllerState) {
2943
+ if (constraintContext.mode !== "rebound") {
2944
+ return null;
2945
+ }
2946
+ const nextViewportProps = nextControllerState.getViewportProps();
2947
+ const shouldRebound = Object.keys(nextViewportProps).some(
2948
+ (key) => !deepEqual(this.props[key], nextViewportProps[key], 1)
2949
+ );
2950
+ return shouldRebound ? {
2951
+ ...this._getTransitionProps(),
2952
+ transitionDuration: REBOUND_DURATION,
2953
+ transitionEasing: EASE_OUT_EXPONENTIAL
2954
+ } : null;
2955
+ }
2956
+ /* Event handlers */
2957
+ // Default handler for the `panstart` event.
2958
+ _onPanStart(event) {
2959
+ const pos = this.getCenter(event);
2960
+ if (!this.isPointInBounds(pos, event)) {
2961
+ return false;
2962
+ }
2963
+ let alternateMode = this.isFunctionKeyPressed(event) || event.rightButton || false;
2964
+ if (this.invertPan || this.dragMode === "pan") {
2965
+ alternateMode = !alternateMode;
2966
+ }
2967
+ const action = alternateMode ? "pan" : "rotate";
2968
+ const constraintContext = this._getConstraintContext(action, "start");
2969
+ const newControllerState = alternateMode ? this.controllerState.panStart({ pos }, constraintContext) : this.controllerState.rotateStart({ pos }, constraintContext);
2970
+ this._panMove = alternateMode;
2971
+ this.updateViewport(newControllerState, NO_TRANSITION_PROPS, { isDragging: true });
2972
+ return true;
2973
+ }
2974
+ // Default handler for the `panmove` and `panend` event.
2975
+ _onPan(event) {
2976
+ if (!this.isDragging()) {
2977
+ return false;
2978
+ }
2979
+ return this._panMove ? this._onPanMove(event) : this._onPanRotate(event);
2980
+ }
2981
+ _onPanEnd(event) {
2982
+ if (!this.isDragging()) {
2983
+ return false;
2984
+ }
2985
+ return this._panMove ? this._onPanMoveEnd(event) : this._onPanRotateEnd(event);
2986
+ }
2987
+ // Default handler for panning to move.
2988
+ // Called by `_onPan` when panning without function key pressed.
2989
+ _onPanMove(event) {
2990
+ if (!this.dragPan) {
2991
+ return false;
2992
+ }
2993
+ const pos = this.getCenter(event);
2994
+ const newControllerState = this.controllerState.pan(
2995
+ { pos },
2996
+ this._getConstraintContext("pan", "update")
2997
+ );
2998
+ this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
2999
+ isDragging: true,
3000
+ isPanning: true
3001
+ });
3002
+ return true;
3003
+ }
3004
+ _onPanMoveEnd(event) {
3005
+ const { inertia } = this;
3006
+ if (this.dragPan && inertia && event.velocity) {
3007
+ const pos = this.getCenter(event);
3008
+ const endPos = [
3009
+ pos[0] + event.velocityX * inertia / 2,
3010
+ pos[1] + event.velocityY * inertia / 2
3011
+ ];
3012
+ const newControllerState = this.controllerState.pan({ pos: endPos }).panEnd();
3013
+ this.updateViewport(
3014
+ newControllerState,
3015
+ {
3016
+ ...this._getTransitionProps(),
3017
+ transitionDuration: inertia,
3018
+ transitionEasing: INERTIA_EASING
3019
+ },
3020
+ {
3021
+ isDragging: false,
3022
+ isPanning: true
3023
+ }
3024
+ );
3025
+ } else {
3026
+ const currentControllerState = this.controllerState;
3027
+ const constraintContext = this._getConstraintContext("pan", "end");
3028
+ const newControllerState = currentControllerState.panEnd(constraintContext);
3029
+ const reboundTransition = this._getReboundTransition(constraintContext, newControllerState);
3030
+ this.updateViewport(newControllerState, reboundTransition, {
3031
+ isDragging: false,
3032
+ isPanning: Boolean(reboundTransition)
3033
+ });
3034
+ }
3035
+ return true;
3036
+ }
3037
+ // Default handler for panning to rotate.
3038
+ // Called by `_onPan` when panning with function key pressed.
3039
+ _onPanRotate(event) {
3040
+ if (!this.dragRotate) {
3041
+ return false;
3042
+ }
3043
+ const pos = this.getCenter(event);
3044
+ const newControllerState = this.controllerState.rotate(
3045
+ { pos },
3046
+ this._getConstraintContext("rotate", "update")
3047
+ );
3048
+ this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
3049
+ isDragging: true,
3050
+ isRotating: true
3051
+ });
3052
+ return true;
3053
+ }
3054
+ _onPanRotateEnd(event) {
3055
+ const { inertia } = this;
3056
+ if (this.dragRotate && inertia && event.velocity) {
3057
+ const pos = this.getCenter(event);
3058
+ const endPos = [
3059
+ pos[0] + event.velocityX * inertia / 2,
3060
+ pos[1] + event.velocityY * inertia / 2
3061
+ ];
3062
+ const newControllerState = this.controllerState.rotate({ pos: endPos }).rotateEnd();
3063
+ this.updateViewport(
3064
+ newControllerState,
3065
+ {
3066
+ ...this._getTransitionProps(),
3067
+ transitionDuration: inertia,
3068
+ transitionEasing: INERTIA_EASING
3069
+ },
3070
+ {
3071
+ isDragging: false,
3072
+ isRotating: true
3073
+ }
3074
+ );
3075
+ } else {
3076
+ const currentControllerState = this.controllerState;
3077
+ const constraintContext = this._getConstraintContext("rotate", "end");
3078
+ const newControllerState = currentControllerState.rotateEnd(constraintContext);
3079
+ const reboundTransition = this._getReboundTransition(constraintContext, newControllerState);
3080
+ this.updateViewport(newControllerState, reboundTransition, {
3081
+ isDragging: false,
3082
+ isRotating: Boolean(reboundTransition)
3083
+ });
3084
+ }
3085
+ return true;
3086
+ }
3087
+ // Default handler for the `wheel` event.
3088
+ _onWheel(event) {
3089
+ if (!this.scrollZoom) {
3090
+ return false;
3091
+ }
3092
+ if (this.trackpadGesture && event.device !== "mouse") {
3093
+ return false;
3094
+ }
3095
+ const pos = this.getCenter(event);
3096
+ if (!this.isPointInBounds(pos, event)) {
3097
+ return false;
3098
+ }
3099
+ event.srcEvent.preventDefault();
3100
+ const { speed = 0.01, smooth = false } = this.scrollZoom === true ? {} : this.scrollZoom;
3101
+ const { delta } = event;
3102
+ let scale = 2 / (1 + Math.exp(-Math.abs(delta * speed)));
3103
+ if (delta < 0 && scale !== 0) {
3104
+ scale = 1 / scale;
3105
+ }
3106
+ const zoomPosition = this.getZoomPosition(pos);
3107
+ const transitionProps = smooth ? { ...this._getTransitionProps({ around: zoomPosition }), transitionDuration: 250 } : NO_TRANSITION_PROPS;
3108
+ const newControllerState = this.controllerState.zoom({ pos: zoomPosition, scale });
3109
+ this.updateViewport(newControllerState, transitionProps, {
3110
+ isZooming: true,
3111
+ isPanning: true
3112
+ });
3113
+ if (!smooth) {
3114
+ this._setInteractionState({ isZooming: false, isPanning: false });
3115
+ }
3116
+ return true;
3117
+ }
3118
+ _onMultiPanStart(event) {
3119
+ const { multiTouchDrag } = this;
3120
+ if (!multiTouchDrag || !this._isMultiPanEventAllowed(event, multiTouchDrag)) {
3121
+ return false;
3122
+ }
3123
+ const currentCenter = event.offsetCenter;
3124
+ if (!this.isPointInBounds(this.getCenter(event), event)) {
3125
+ return false;
3126
+ }
3127
+ const isTrackpad = event.pointerType === "trackpad";
3128
+ const startCenter = {
3129
+ x: currentCenter.x - (isTrackpad ? 0 : event.deltaX),
3130
+ y: currentCenter.y - (isTrackpad ? 0 : event.deltaY)
3131
+ };
3132
+ const startEvent = { ...event, offsetCenter: startCenter };
3133
+ const pos = this.getCenter(startEvent);
3134
+ const newControllerState = multiTouchDrag === "pan" ? this.controllerState.panStart({ pos }, this._getConstraintContext("pan", "start")) : this.controllerState.rotateStart({ pos }, this._getConstraintContext("rotate", "start"));
3135
+ this._multiPanMode = multiTouchDrag;
3136
+ this._multiPanStartCenter = startCenter;
3137
+ this.updateViewport(newControllerState, NO_TRANSITION_PROPS, { isDragging: true });
3138
+ return true;
3139
+ }
3140
+ _onMultiPan(event) {
3141
+ const { mode, event: panEvent } = this._getMultiPanEvent(event);
3142
+ if (!mode || !panEvent || !this.isDragging()) {
3143
+ return false;
3144
+ }
3145
+ return mode === "pan" ? this._onPanMove(panEvent) : this._onPanRotate(panEvent);
3146
+ }
3147
+ _onMultiPanEnd(event) {
3148
+ const { mode, event: panEvent } = this._getMultiPanEvent(event);
3149
+ if (!mode || !panEvent || !this.isDragging()) {
3150
+ this._resetMultiPan();
3151
+ return false;
3152
+ }
3153
+ const handled = mode === "pan" ? this._onPanMoveEnd(panEvent) : this._onPanRotateEnd(panEvent);
3154
+ this._resetMultiPan();
3155
+ return handled;
3156
+ }
3157
+ _isTrackpadGestureAllowed(event) {
3158
+ return event.pointerType !== "trackpad" || this.trackpadGesture;
3159
+ }
3160
+ _isMultiPanEventAllowed(event, mode) {
3161
+ if (event.pointerType === "trackpad") {
3162
+ return this.trackpadGesture && (mode === "pan" ? this.dragPan : this.dragRotate);
3163
+ }
3164
+ return event.pointerType === "touch" && (mode === "pan" ? this.dragPan : this.dragRotate);
3165
+ }
3166
+ _getMultiPanEvent(event) {
3167
+ const mode = this._multiPanMode;
3168
+ const startCenter = this._multiPanStartCenter;
3169
+ if (!mode || !startCenter) {
3170
+ return { mode: null, event: null };
3171
+ }
3172
+ return {
3173
+ mode,
3174
+ event: {
3175
+ ...event,
3176
+ offsetCenter: {
3177
+ x: startCenter.x + event.deltaX,
3178
+ y: startCenter.y + event.deltaY
3179
+ }
3180
+ }
3181
+ };
3182
+ }
3183
+ _resetMultiPan() {
3184
+ this._multiPanMode = null;
3185
+ this._multiPanStartCenter = null;
3186
+ }
3187
+ // Default handler for the `pinchstart` event.
3188
+ _onPinchStart(event) {
3189
+ this._doubleClickDragAnchor = null;
3190
+ const pos = this.getCenter(event);
3191
+ if (!this.isPointInBounds(pos, event)) {
3192
+ return false;
3193
+ }
3194
+ const newControllerState = this.controllerState.zoomStart({ pos: this.getZoomPosition(pos) }, this._getConstraintContext("zoom", "start")).rotateStart({ pos }, this._getConstraintContext("rotate", "start"));
3195
+ pinchEventWorkaround._startPinchRotation = event.rotation;
3196
+ pinchEventWorkaround._lastPinchEvent = event;
3197
+ this.updateViewport(newControllerState, NO_TRANSITION_PROPS, { isDragging: true });
3198
+ return true;
3199
+ }
3200
+ // Default handler for the `pinchmove` and `pinchend` events.
3201
+ _onPinch(event) {
3202
+ if (!this.touchZoom && !this.touchRotate) {
3203
+ return false;
3204
+ }
3205
+ if (!this.isDragging()) {
3206
+ return false;
3207
+ }
3208
+ let newControllerState = this.controllerState;
3209
+ if (this.touchZoom) {
3210
+ const { scale } = event;
3211
+ const pos = this.getCenter(event);
3212
+ newControllerState = newControllerState.zoom(
3213
+ { pos: this.getZoomPosition(pos), scale },
3214
+ this._getConstraintContext("zoom", "update")
3215
+ );
3216
+ }
3217
+ if (this.touchRotate) {
3218
+ const { rotation } = event;
3219
+ newControllerState = newControllerState.rotate(
3220
+ { deltaAngleX: pinchEventWorkaround._startPinchRotation - rotation },
3221
+ this._getConstraintContext("rotate", "update")
3222
+ );
3223
+ }
3224
+ this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
3225
+ isDragging: true,
3226
+ isPanning: this.touchZoom,
3227
+ isZooming: this.touchZoom,
3228
+ isRotating: this.touchRotate
3229
+ });
3230
+ pinchEventWorkaround._lastPinchEvent = event;
3231
+ return true;
3232
+ }
3233
+ _onPinchEnd(event) {
3234
+ if (!this.isDragging()) {
3235
+ return false;
3236
+ }
3237
+ const { inertia } = this;
3238
+ const { _lastPinchEvent } = pinchEventWorkaround;
3239
+ if (this.touchZoom && inertia && _lastPinchEvent && event.scale !== _lastPinchEvent.scale) {
3240
+ const pos = this.getCenter(event);
3241
+ const zoomPosition = this.getZoomPosition(pos);
3242
+ let newControllerState = this.controllerState.rotateEnd();
3243
+ const z = Math.log2(event.scale);
3244
+ const velocityZ = (z - Math.log2(_lastPinchEvent.scale)) / (event.deltaTime - _lastPinchEvent.deltaTime);
3245
+ const endScale = Math.pow(2, z + velocityZ * inertia / 2);
3246
+ newControllerState = newControllerState.zoom({ pos: zoomPosition, scale: endScale }).zoomEnd();
3247
+ this.updateViewport(
3248
+ newControllerState,
3249
+ {
3250
+ ...this._getTransitionProps({ around: zoomPosition }),
3251
+ transitionDuration: inertia,
3252
+ transitionEasing: INERTIA_EASING
3253
+ },
3254
+ {
3255
+ isDragging: false,
3256
+ isPanning: this.touchZoom,
3257
+ isZooming: this.touchZoom,
3258
+ isRotating: false
3259
+ }
3260
+ );
3261
+ this.blockEvents(inertia);
3262
+ } else {
3263
+ const currentControllerState = this.controllerState;
3264
+ const zoomConstraintContext = this._getConstraintContext("zoom", "end");
3265
+ const rotateConstraintContext = this._getConstraintContext("rotate", "end");
3266
+ const newControllerState = currentControllerState.zoomEnd(zoomConstraintContext).rotateEnd(rotateConstraintContext);
3267
+ const reboundTransition = this._getReboundTransition(
3268
+ this.touchZoom ? zoomConstraintContext : rotateConstraintContext,
3269
+ newControllerState
3270
+ );
3271
+ this.updateViewport(newControllerState, reboundTransition, {
3272
+ isDragging: false,
3273
+ isPanning: Boolean(reboundTransition) && this.touchZoom,
3274
+ isZooming: Boolean(reboundTransition) && this.touchZoom,
3275
+ isRotating: Boolean(reboundTransition) && this.touchRotate
3276
+ });
3277
+ }
3278
+ pinchEventWorkaround._startPinchRotation = null;
3279
+ pinchEventWorkaround._lastPinchEvent = null;
3280
+ return true;
3281
+ }
3282
+ // Default handler for the `dblclick` event.
3283
+ _onDoubleClick(event) {
3284
+ if (!this.doubleClickZoom) {
3285
+ return false;
3286
+ }
3287
+ if (Date.now() < this._suppressDoubleClickUntil) {
3288
+ return false;
3289
+ }
3290
+ const pos = this.getCenter(event);
3291
+ if (!this.isPointInBounds(pos, event)) {
3292
+ return false;
3293
+ }
3294
+ const isZoomOut = this.isFunctionKeyPressed(event);
3295
+ const zoomPosition = this.getZoomPosition(pos);
3296
+ const newControllerState = this.controllerState.zoom({
3297
+ pos: zoomPosition,
3298
+ scale: isZoomOut ? 0.5 : 2
3299
+ });
3300
+ this.updateViewport(newControllerState, this._getTransitionProps({ around: zoomPosition }), {
3301
+ isZooming: true,
3302
+ isPanning: true
3303
+ });
3304
+ this.blockEvents(100);
3305
+ return true;
3306
+ }
3307
+ _onDoubleClickDragStart(event) {
3308
+ if (!this.doubleClickDragZoom) {
3309
+ this._doubleClickDragAnchor = null;
3310
+ return false;
3311
+ }
3312
+ const pos = this.getCenter(event);
3313
+ if (!this.isPointInBounds(pos, event)) {
3314
+ this._doubleClickDragAnchor = null;
3315
+ return false;
3316
+ }
3317
+ this._doubleClickDragAnchor = this.getZoomPosition(pos);
3318
+ let newControllerState = this.controllerState.zoomStart(
3319
+ { pos: this._doubleClickDragAnchor },
3320
+ this._getConstraintContext("zoom", "start")
3321
+ );
3322
+ if (event.scale !== 1) {
3323
+ newControllerState = newControllerState.zoom(
3324
+ { pos: this._doubleClickDragAnchor, scale: event.scale },
3325
+ this._getConstraintContext("zoom", "update")
3326
+ );
3327
+ }
3328
+ this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
3329
+ isDragging: true,
3330
+ isPanning: true,
3331
+ isZooming: true
3332
+ });
3333
+ return true;
3334
+ }
3335
+ _onDoubleClickDrag(event) {
3336
+ const pos = this._doubleClickDragAnchor;
3337
+ if (!pos) {
3338
+ return false;
3339
+ }
3340
+ const newControllerState = this.controllerState.zoom(
3341
+ { pos, scale: event.scale },
3342
+ this._getConstraintContext("zoom", "update")
3343
+ );
3344
+ this.updateViewport(newControllerState, NO_TRANSITION_PROPS, {
3345
+ isDragging: true,
3346
+ isPanning: true,
3347
+ isZooming: true
3348
+ });
3349
+ return true;
3350
+ }
3351
+ _onDoubleClickDragEnd(_event) {
3352
+ const pos = this._doubleClickDragAnchor;
3353
+ if (!pos) {
3354
+ return false;
3355
+ }
3356
+ this._doubleClickDragAnchor = null;
3357
+ const currentControllerState = this.controllerState;
3358
+ const constraintContext = this._getConstraintContext("zoom", "end");
3359
+ const newControllerState = currentControllerState.zoomEnd(constraintContext);
3360
+ const reboundTransition = this._getReboundTransition(constraintContext, newControllerState);
3361
+ this.updateViewport(newControllerState, reboundTransition, {
3362
+ isDragging: false,
3363
+ isPanning: Boolean(reboundTransition),
3364
+ isZooming: Boolean(reboundTransition)
3365
+ });
3366
+ this._suppressDoubleClickUntil = Date.now() + 100;
3367
+ this.blockEvents(100);
3368
+ return true;
3369
+ }
3370
+ // Default handler for the `keydown` event
3371
+ _onKeyDown(event) {
3372
+ if (!this.keyboard) {
3373
+ return false;
3374
+ }
3375
+ const funcKey = this.isFunctionKeyPressed(event);
3376
+ const { zoomSpeed, moveSpeed, rotateSpeedX, rotateSpeedY } = this.keyboard === true ? {} : this.keyboard;
3377
+ const { controllerState } = this;
3378
+ let newControllerState;
3379
+ const interactionState = {};
3380
+ switch (event.srcEvent.code) {
3381
+ case "Minus":
3382
+ newControllerState = funcKey ? controllerState.zoomOut(zoomSpeed).zoomOut(zoomSpeed) : controllerState.zoomOut(zoomSpeed);
3383
+ interactionState.isZooming = true;
3384
+ break;
3385
+ case "Equal":
3386
+ newControllerState = funcKey ? controllerState.zoomIn(zoomSpeed).zoomIn(zoomSpeed) : controllerState.zoomIn(zoomSpeed);
3387
+ interactionState.isZooming = true;
3388
+ break;
3389
+ case "ArrowLeft":
3390
+ if (funcKey) {
3391
+ newControllerState = controllerState.rotateLeft(rotateSpeedX);
3392
+ interactionState.isRotating = true;
3393
+ } else {
3394
+ newControllerState = controllerState.moveLeft(moveSpeed);
3395
+ interactionState.isPanning = true;
3396
+ }
3397
+ break;
3398
+ case "ArrowRight":
3399
+ if (funcKey) {
3400
+ newControllerState = controllerState.rotateRight(rotateSpeedX);
3401
+ interactionState.isRotating = true;
3402
+ } else {
3403
+ newControllerState = controllerState.moveRight(moveSpeed);
3404
+ interactionState.isPanning = true;
3405
+ }
3406
+ break;
3407
+ case "ArrowUp":
3408
+ if (funcKey) {
3409
+ newControllerState = controllerState.rotateUp(rotateSpeedY);
3410
+ interactionState.isRotating = true;
3411
+ } else {
3412
+ newControllerState = controllerState.moveUp(moveSpeed);
3413
+ interactionState.isPanning = true;
3414
+ }
3415
+ break;
3416
+ case "ArrowDown":
3417
+ if (funcKey) {
3418
+ newControllerState = controllerState.rotateDown(rotateSpeedY);
3419
+ interactionState.isRotating = true;
3420
+ } else {
3421
+ newControllerState = controllerState.moveDown(moveSpeed);
3422
+ interactionState.isPanning = true;
3423
+ }
3424
+ break;
3425
+ default:
3426
+ return false;
3427
+ }
3428
+ this.updateViewport(newControllerState, this._getTransitionProps(), interactionState);
3429
+ return true;
3430
+ }
3431
+ _getTransitionProps(opts) {
3432
+ const { transition } = this;
3433
+ if (!transition || !transition.transitionInterpolator) {
3434
+ return NO_TRANSITION_PROPS;
3435
+ }
3436
+ return opts ? {
3437
+ ...transition,
3438
+ transitionInterpolator: new LinearInterpolator({
3439
+ ...opts,
3440
+ ...transition.transitionInterpolator.opts,
3441
+ makeViewport: this.controllerState.makeViewport
3442
+ })
3443
+ } : transition;
3444
+ }
3445
+ };
3446
+
3447
+ // ../core/src/controllers/view-state.ts
3448
+ var CONSTRAINT_AROUND = /* @__PURE__ */ Symbol("constraintAround");
3449
+ var ViewState = class {
3450
+ constructor(props, state, makeViewport, constraintContext) {
3451
+ this.makeViewport = makeViewport;
3452
+ this._viewportProps = this.applyConstraints(props, constraintContext);
3453
+ this._state = state;
3454
+ }
3455
+ getViewportProps() {
3456
+ return this._viewportProps;
3457
+ }
3458
+ getState() {
3459
+ return this._state;
3460
+ }
3461
+ };
3462
+
3463
+ // ../core/src/controllers/utils.ts
3464
+ function applyRubberBand(value, constrainedValue, range) {
3465
+ const overshoot = value - constrainedValue;
3466
+ return overshoot && Number.isFinite(overshoot) ? constrainedValue + overshoot * range / (range + Math.abs(overshoot)) : constrainedValue;
3467
+ }
3468
+ function getMaxBoundsRect(width, height, padding) {
3469
+ const left = getPosition(parsePosition((padding == null ? void 0 : padding.left) ?? 0), width);
3470
+ const right = getPosition(parsePosition((padding == null ? void 0 : padding.right) ?? 0), width);
3471
+ const top = getPosition(parsePosition((padding == null ? void 0 : padding.top) ?? 0), height);
3472
+ const bottom = getPosition(parsePosition((padding == null ? void 0 : padding.bottom) ?? 0), height);
3473
+ return {
3474
+ x: left,
3475
+ y: top,
3476
+ width: width - left - right,
3477
+ height: height - top - bottom
3478
+ };
3479
+ }
3480
+ function getMaxBoundsExtents(viewport, target, rect) {
3481
+ let [x, y] = viewport.project(target);
3482
+ x = Number.isFinite(x) ? x : viewport.width / 2;
3483
+ y = Number.isFinite(y) ? y : viewport.height / 2;
3484
+ return {
3485
+ left: x - rect.x,
3486
+ right: rect.x + rect.width - x,
3487
+ top: y - rect.y,
3488
+ bottom: rect.y + rect.height - y
3489
+ };
3490
+ }
3491
+
3492
+ // ../core/src/controllers/map-controller.ts
3493
+ var import_web_mercator4 = require("@math.gl/web-mercator");
3494
+ var PITCH_MOUSE_THRESHOLD = 5;
3495
+ var PITCH_ACCEL = 1.2;
3496
+ var WEB_MERCATOR_TILE_SIZE = 512;
3497
+ var WEB_MERCATOR_MAX_BOUNDS = [
3498
+ [-Infinity, -90],
3499
+ [Infinity, 90]
3500
+ ];
3501
+ var ZOOM_RUBBER_BAND_RANGE = 1;
3502
+ function lngLatToWorld2([lng, lat]) {
3503
+ if (Math.abs(lat) > 90) {
3504
+ lat = Math.sign(lat) * 90;
3505
+ }
3506
+ if (Number.isFinite(lng)) {
3507
+ const [x, y2] = (0, import_web_mercator4.lngLatToWorld)([lng, lat]);
3508
+ return [x, (0, import_core7.clamp)(y2, 0, WEB_MERCATOR_TILE_SIZE)];
3509
+ }
3510
+ const [, y] = (0, import_web_mercator4.lngLatToWorld)([0, lat]);
3511
+ return [lng, (0, import_core7.clamp)(y, 0, WEB_MERCATOR_TILE_SIZE)];
3512
+ }
3513
+ var MapState = class extends ViewState {
3514
+ constructor(options) {
3515
+ const {
3516
+ /** Mapbox viewport properties */
3517
+ /** The width of the viewport */
3518
+ width,
3519
+ /** The height of the viewport */
3520
+ height,
3521
+ /** The latitude at the center of the viewport */
3522
+ latitude,
3523
+ /** The longitude at the center of the viewport */
3524
+ longitude,
3525
+ /** The tile zoom level of the map. */
3526
+ zoom,
3527
+ /** The bearing of the viewport in degrees */
3528
+ bearing = 0,
3529
+ /** The pitch of the viewport in degrees */
3530
+ pitch = 0,
3531
+ /**
3532
+ * Specify the altitude of the viewport camera
3533
+ * Unit: map heights, default 1.5
3534
+ * Non-public API, see https://github.com/mapbox/mapbox-gl-js/issues/1137
3535
+ */
3536
+ altitude = 1.5,
3537
+ /** Viewport position */
3538
+ position = [0, 0, 0],
3539
+ /** Viewport constraints */
3540
+ maxZoom = 20,
3541
+ minZoom = 0,
3542
+ maxPitch = 60,
3543
+ minPitch = 0,
3544
+ /** Interaction states, required to calculate change during transform */
3545
+ /* The point on map being grabbed when the operation first started */
3546
+ startPanLngLat,
3547
+ /* Center of the zoom when the operation first started */
3548
+ startZoomLngLat,
3549
+ /* Pointer position when rotation started */
3550
+ startRotatePos,
3551
+ /* The lng/lat point at the rotation pivot (where rotation started) */
3552
+ startRotateLngLat,
3553
+ /** Bearing when current perspective rotate operation started */
3554
+ startBearing,
3555
+ /** Pitch when current perspective rotate operation started */
3556
+ startPitch,
3557
+ /** Zoom when current zoom operation started */
3558
+ startZoom,
3559
+ /** Normalize viewport props to fit map height into viewport */
3560
+ normalize = true,
3561
+ rubberBand = false
3562
+ } = options;
3563
+ const { [CONSTRAINT_AROUND]: constraintAround } = options;
3564
+ assert(Number.isFinite(longitude));
3565
+ assert(Number.isFinite(latitude));
3566
+ assert(Number.isFinite(zoom));
3567
+ const maxBounds = options.maxBounds || (normalize ? WEB_MERCATOR_MAX_BOUNDS : null);
3568
+ const maxBoundsPadding = options.maxBoundsPadding || null;
3569
+ super(
3570
+ {
3571
+ width,
3572
+ height,
3573
+ latitude,
3574
+ longitude,
3575
+ zoom,
3576
+ bearing,
3577
+ pitch,
3578
+ altitude,
3579
+ maxZoom,
3580
+ minZoom,
3581
+ maxPitch,
3582
+ minPitch,
3583
+ normalize,
3584
+ position,
3585
+ maxBounds,
3586
+ maxBoundsPadding,
3587
+ rubberBand,
3588
+ ...{ [CONSTRAINT_AROUND]: constraintAround }
3589
+ },
3590
+ {
3591
+ startPanLngLat,
3592
+ startZoomLngLat,
3593
+ startRotatePos,
3594
+ startRotateLngLat,
3595
+ startBearing,
3596
+ startPitch,
3597
+ startZoom
3598
+ },
3599
+ options.makeViewport,
3600
+ options.constraintContext
3601
+ );
3602
+ this.getAltitude = options.getAltitude;
3603
+ }
3604
+ /**
3605
+ * Start panning
3606
+ * @param {[Number, Number]} pos - position on screen where the pointer grabs
3607
+ */
3608
+ panStart({ pos }, constraintContext) {
3609
+ return this._getUpdatedState(
3610
+ {
3611
+ startPanLngLat: this._unproject(pos)
3612
+ },
3613
+ constraintContext
3614
+ );
3615
+ }
3616
+ /**
3617
+ * Pan
3618
+ * @param {[Number, Number]} pos - position on screen where the pointer is
3619
+ * @param {[Number, Number], optional} startPos - where the pointer grabbed at
3620
+ * the start of the operation. Must be supplied of `panStart()` was not called
3621
+ */
3622
+ pan({ pos, startPos }, constraintContext) {
3623
+ const startPanLngLat = this.getState().startPanLngLat || this._unproject(startPos);
3624
+ if (!startPanLngLat) {
3625
+ return this;
3626
+ }
3627
+ const viewport = this.makeViewport(this.getViewportProps());
3628
+ const newProps = viewport.panByPosition(startPanLngLat, pos);
3629
+ return this._getUpdatedState(newProps, constraintContext);
3630
+ }
3631
+ /**
3632
+ * End panning
3633
+ * Must call if `panStart()` was called
3634
+ */
3635
+ panEnd(constraintContext) {
3636
+ return this._getUpdatedState(
3637
+ {
3638
+ startPanLngLat: null
3639
+ },
3640
+ constraintContext
3641
+ );
3642
+ }
3643
+ /**
3644
+ * Start rotating
3645
+ * @param {[Number, Number]} pos - position on screen where the center is
3646
+ */
3647
+ rotateStart({ pos }) {
3648
+ var _a2;
3649
+ const altitude = (_a2 = this.getAltitude) == null ? void 0 : _a2.call(this, pos);
3650
+ return this._getUpdatedState({
3651
+ startRotatePos: pos,
3652
+ startRotateLngLat: altitude !== void 0 ? this._unproject3D(pos, altitude) : void 0,
3653
+ startBearing: this.getViewportProps().bearing,
3654
+ startPitch: this.getViewportProps().pitch
3655
+ });
3656
+ }
3657
+ /**
3658
+ * Rotate
3659
+ * @param {[Number, Number]} pos - position on screen where the center is
3660
+ */
3661
+ rotate({
3662
+ pos,
3663
+ deltaAngleX = 0,
3664
+ deltaAngleY = 0
3665
+ }) {
3666
+ const { startRotatePos, startRotateLngLat, startBearing, startPitch } = this.getState();
3667
+ if (!startRotatePos || startBearing === void 0 || startPitch === void 0) {
3668
+ return this;
3669
+ }
3670
+ let newRotation;
3671
+ if (pos) {
3672
+ newRotation = this._getNewRotation(pos, startRotatePos, startPitch, startBearing);
3673
+ } else {
3674
+ newRotation = {
3675
+ bearing: startBearing + deltaAngleX,
3676
+ pitch: startPitch + deltaAngleY
3677
+ };
3678
+ }
3679
+ if (startRotateLngLat) {
3680
+ const rotatedViewport = this.makeViewport({
3681
+ ...this.getViewportProps(),
3682
+ ...newRotation
3683
+ });
3684
+ const panMethod = "panByPosition3D" in rotatedViewport ? "panByPosition3D" : "panByPosition";
3685
+ return this._getUpdatedState({
3686
+ ...newRotation,
3687
+ ...rotatedViewport[panMethod](startRotateLngLat, startRotatePos)
3688
+ });
3689
+ }
3690
+ return this._getUpdatedState(newRotation);
3691
+ }
3692
+ /**
3693
+ * End rotating
3694
+ * Must call if `rotateStart()` was called
3695
+ */
3696
+ rotateEnd() {
3697
+ return this._getUpdatedState({
3698
+ startRotatePos: null,
3699
+ startRotateLngLat: null,
3700
+ startBearing: null,
3701
+ startPitch: null
3702
+ });
3703
+ }
3704
+ /**
3705
+ * Start zooming
3706
+ * @param {[Number, Number]} pos - position on screen where the center is
3707
+ */
3708
+ zoomStart({ pos }, constraintContext) {
3709
+ return this._getUpdatedState(
3710
+ {
3711
+ startZoomLngLat: this._unproject(pos),
3712
+ startZoom: this.getViewportProps().zoom
3713
+ },
3714
+ constraintContext
3715
+ );
3716
+ }
3717
+ /**
3718
+ * Zoom
3719
+ * @param {[Number, Number]} pos - position on screen where the current center is
3720
+ * @param {[Number, Number]} startPos - the center position at
3721
+ * the start of the operation. Must be supplied of `zoomStart()` was not called
3722
+ * @param {Number} scale - a number between [0, 1] specifying the accumulated
3723
+ * relative scale.
3724
+ */
3725
+ zoom({
3726
+ pos,
3727
+ startPos,
3728
+ scale
3729
+ }, constraintContext) {
3730
+ let { startZoom, startZoomLngLat } = this.getState();
3731
+ if (!startZoomLngLat) {
3732
+ startZoom = this.getViewportProps().zoom;
3733
+ startZoomLngLat = this._unproject(startPos) || this._unproject(pos);
3734
+ }
3735
+ if (!startZoomLngLat) {
3736
+ return this;
3737
+ }
3738
+ return this._getUpdatedState(
3739
+ {
3740
+ zoom: startZoom + Math.log2(scale),
3741
+ [CONSTRAINT_AROUND]: { position: startZoomLngLat, screenPosition: pos }
3742
+ },
3743
+ constraintContext
3744
+ );
3745
+ }
3746
+ /**
3747
+ * End zooming
3748
+ * Must call if `zoomStart()` was called
3749
+ */
3750
+ zoomEnd(constraintContext) {
3751
+ return this._getUpdatedState(
3752
+ {
3753
+ startZoomLngLat: null,
3754
+ startZoom: null
3755
+ },
3756
+ constraintContext
3757
+ );
3758
+ }
3759
+ zoomIn(speed = 2, constraintContext) {
3760
+ return this._zoomFromCenter(speed, constraintContext);
3761
+ }
3762
+ zoomOut(speed = 2, constraintContext) {
3763
+ return this._zoomFromCenter(1 / speed, constraintContext);
3764
+ }
3765
+ moveLeft(speed = 100, constraintContext) {
3766
+ return this._panFromCenter([speed, 0], constraintContext);
3767
+ }
3768
+ moveRight(speed = 100, constraintContext) {
3769
+ return this._panFromCenter([-speed, 0], constraintContext);
3770
+ }
3771
+ moveUp(speed = 100, constraintContext) {
3772
+ return this._panFromCenter([0, speed], constraintContext);
3773
+ }
3774
+ moveDown(speed = 100, constraintContext) {
3775
+ return this._panFromCenter([0, -speed], constraintContext);
3776
+ }
3777
+ rotateLeft(speed = 15) {
3778
+ return this._getUpdatedState({
3779
+ bearing: this.getViewportProps().bearing - speed
3780
+ });
3781
+ }
3782
+ rotateRight(speed = 15) {
3783
+ return this._getUpdatedState({
3784
+ bearing: this.getViewportProps().bearing + speed
3785
+ });
3786
+ }
3787
+ rotateUp(speed = 10) {
3788
+ return this._getUpdatedState({
3789
+ pitch: this.getViewportProps().pitch + speed
3790
+ });
3791
+ }
3792
+ rotateDown(speed = 10) {
3793
+ return this._getUpdatedState({
3794
+ pitch: this.getViewportProps().pitch - speed
3795
+ });
3796
+ }
3797
+ shortestPathFrom(viewState) {
3798
+ const fromProps = viewState.getViewportProps();
3799
+ const props = { ...this.getViewportProps() };
3800
+ const { bearing, longitude } = props;
3801
+ if (Math.abs(bearing - fromProps.bearing) > 180) {
3802
+ props.bearing = bearing < 0 ? bearing + 360 : bearing - 360;
3803
+ }
3804
+ if (Math.abs(longitude - fromProps.longitude) > 180) {
3805
+ props.longitude = longitude < 0 ? longitude + 360 : longitude - 360;
3806
+ }
3807
+ return props;
3808
+ }
3809
+ // Apply any constraints (mathematical or defined by _viewportProps) to map state
3810
+ applyConstraints(props, constraintContext) {
3811
+ const internalProps = props;
3812
+ const constraintAround = internalProps[CONSTRAINT_AROUND];
3813
+ delete internalProps[CONSTRAINT_AROUND];
3814
+ const { maxPitch, minPitch, pitch, bearing, normalize, maxBounds, rubberBand } = props;
3815
+ if (normalize) {
3816
+ if (bearing < -180 || bearing > 180) {
3817
+ props.bearing = mod(bearing + 180, 360) - 180;
3818
+ }
3819
+ }
3820
+ props.pitch = (0, import_core7.clamp)(pitch, minPitch, maxPitch);
3821
+ const constrainedZoom = this._constrainZoom(props.zoom, props);
3822
+ const shouldRubberBand = rubberBand && (constraintContext == null ? void 0 : constraintContext.mode) === "elastic";
3823
+ props.zoom = (constraintContext == null ? void 0 : constraintContext.mode) === "preserve" ? props.zoom : shouldRubberBand ? applyRubberBand(props.zoom, constrainedZoom, ZOOM_RUBBER_BAND_RANGE) : constrainedZoom;
3824
+ if (constraintAround) {
3825
+ const viewport = this.makeViewport(props);
3826
+ Object.assign(
3827
+ props,
3828
+ viewport.panByPosition(constraintAround.position, constraintAround.screenPosition)
3829
+ );
3830
+ }
3831
+ if (normalize && (props.longitude < -180 || props.longitude > 180)) {
3832
+ props.longitude = mod(props.longitude + 180, 360) - 180;
3833
+ }
3834
+ if (maxBounds) {
3835
+ const maxBoundsRect = getMaxBoundsRect(props.width, props.height, props.maxBoundsPadding);
3836
+ const viewport = this.makeViewport({ ...props, bearing: 0, pitch: 0 });
3837
+ const screenExtents = getMaxBoundsExtents(
3838
+ viewport,
3839
+ [props.longitude, props.latitude],
3840
+ maxBoundsRect
3841
+ );
3842
+ const bl = lngLatToWorld2(maxBounds[0]);
3843
+ const tr = lngLatToWorld2(maxBounds[1]);
3844
+ const scale = 2 ** props.zoom;
3845
+ const minimumCenter = [
3846
+ bl[0] + screenExtents.left / scale,
3847
+ bl[1] + screenExtents.bottom / scale
3848
+ ];
3849
+ const maximumCenter = [
3850
+ tr[0] - screenExtents.right / scale,
3851
+ tr[1] - screenExtents.top / scale
3852
+ ];
3853
+ const center = lngLatToWorld2([props.longitude, props.latitude]);
3854
+ const constrainedCenter = [
3855
+ (0, import_core7.clamp)(center[0], minimumCenter[0], maximumCenter[0]),
3856
+ (0, import_core7.clamp)(center[1], minimumCenter[1], maximumCenter[1])
3857
+ ];
3858
+ const displayedCenter = center.slice();
3859
+ if (maxBoundsRect.width >= 0) {
3860
+ displayedCenter[0] = (constraintContext == null ? void 0 : constraintContext.mode) === "preserve" ? center[0] : shouldRubberBand ? applyRubberBand(center[0], constrainedCenter[0], maxBoundsRect.width / 2 / scale) : constrainedCenter[0];
3861
+ }
3862
+ if (maxBoundsRect.height >= 0) {
3863
+ displayedCenter[1] = (constraintContext == null ? void 0 : constraintContext.mode) === "preserve" ? center[1] : shouldRubberBand ? applyRubberBand(center[1], constrainedCenter[1], maxBoundsRect.height / 2 / scale) : constrainedCenter[1];
3864
+ }
3865
+ if (displayedCenter[0] !== center[0] || displayedCenter[1] !== center[1]) {
3866
+ const [displayedLongitude, displayedLatitude] = (0, import_web_mercator4.worldToLngLat)(displayedCenter);
3867
+ if (displayedCenter[0] !== center[0]) {
3868
+ props.longitude = displayedLongitude;
3869
+ }
3870
+ if (displayedCenter[1] !== center[1]) {
3871
+ props.latitude = displayedLatitude;
3872
+ }
3873
+ }
3874
+ }
3875
+ return props;
3876
+ }
3877
+ /* Private methods */
3878
+ _constrainZoom(zoom, props) {
3879
+ props ||= this.getViewportProps();
3880
+ const { maxZoom, maxBounds } = props;
3881
+ const shouldApplyMaxBounds = maxBounds !== null && props.width > 0 && props.height > 0;
3882
+ let { minZoom } = props;
3883
+ if (shouldApplyMaxBounds) {
3884
+ const maxBoundsRect = getMaxBoundsRect(props.width, props.height, props.maxBoundsPadding);
3885
+ const bl = lngLatToWorld2(maxBounds[0]);
3886
+ const tr = lngLatToWorld2(maxBounds[1]);
3887
+ const w = tr[0] - bl[0];
3888
+ const h = tr[1] - bl[1];
3889
+ if (maxBoundsRect.width > 0 && Number.isFinite(w) && w > 0) {
3890
+ minZoom = Math.max(minZoom, Math.log2(maxBoundsRect.width / w));
3891
+ }
3892
+ if (maxBoundsRect.height > 0 && Number.isFinite(h) && h > 0) {
3893
+ minZoom = Math.max(minZoom, Math.log2(maxBoundsRect.height / h));
3894
+ }
3895
+ if (minZoom > maxZoom) minZoom = maxZoom;
3896
+ }
3897
+ return (0, import_core7.clamp)(zoom, minZoom, maxZoom);
3898
+ }
3899
+ _zoomFromCenter(scale, constraintContext) {
3900
+ const { width, height } = this.getViewportProps();
3901
+ return this.zoom(
3902
+ {
3903
+ pos: [width / 2, height / 2],
3904
+ scale
3905
+ },
3906
+ constraintContext
3907
+ );
3908
+ }
3909
+ _panFromCenter(offset, constraintContext) {
3910
+ const { width, height } = this.getViewportProps();
3911
+ return this.pan(
3912
+ {
3913
+ startPos: [width / 2, height / 2],
3914
+ pos: [width / 2 + offset[0], height / 2 + offset[1]]
3915
+ },
3916
+ constraintContext
3917
+ );
3918
+ }
3919
+ _getUpdatedState(newProps, constraintContext) {
3920
+ return new this.constructor({
3921
+ makeViewport: this.makeViewport,
3922
+ ...this.getViewportProps(),
3923
+ ...this.getState(),
3924
+ ...newProps,
3925
+ constraintContext
3926
+ });
3927
+ }
3928
+ _unproject(pos) {
3929
+ const viewport = this.makeViewport(this.getViewportProps());
3930
+ return pos && viewport.unproject(pos);
3931
+ }
3932
+ _unproject3D(pos, altitude) {
3933
+ const viewport = this.makeViewport(this.getViewportProps());
3934
+ return viewport.unproject(pos, { targetZ: altitude });
3935
+ }
3936
+ _getNewRotation(pos, startPos, startPitch, startBearing) {
3937
+ const deltaX = pos[0] - startPos[0];
3938
+ const deltaY = pos[1] - startPos[1];
3939
+ const centerY = pos[1];
3940
+ const startY = startPos[1];
3941
+ const { width, height } = this.getViewportProps();
3942
+ const deltaScaleX = deltaX / width;
3943
+ let deltaScaleY = 0;
3944
+ if (deltaY > 0) {
3945
+ if (Math.abs(height - startY) > PITCH_MOUSE_THRESHOLD) {
3946
+ deltaScaleY = deltaY / (startY - height) * PITCH_ACCEL;
3947
+ }
3948
+ } else if (deltaY < 0) {
3949
+ if (startY > PITCH_MOUSE_THRESHOLD) {
3950
+ deltaScaleY = 1 - centerY / startY;
3951
+ }
3952
+ }
3953
+ deltaScaleY = (0, import_core7.clamp)(deltaScaleY, -1, 1);
3954
+ const { minPitch, maxPitch } = this.getViewportProps();
3955
+ const bearing = startBearing + 180 * deltaScaleX;
3956
+ let pitch = startPitch;
3957
+ if (deltaScaleY > 0) {
3958
+ pitch = startPitch + deltaScaleY * (maxPitch - startPitch);
3959
+ } else if (deltaScaleY < 0) {
3960
+ pitch = startPitch - deltaScaleY * (minPitch - startPitch);
3961
+ }
3962
+ return {
3963
+ pitch,
3964
+ bearing
3965
+ };
3966
+ }
3967
+ };
3968
+ var MapController = class extends Controller {
3969
+ constructor() {
3970
+ super(...arguments);
3971
+ this.ControllerState = MapState;
3972
+ this.transition = {
3973
+ transitionDuration: 300,
3974
+ transitionInterpolator: new LinearInterpolator({
3975
+ transitionProps: {
3976
+ compare: ["longitude", "latitude", "zoom", "bearing", "pitch", "position"],
3977
+ required: ["longitude", "latitude", "zoom"]
3978
+ }
3979
+ })
3980
+ };
3981
+ this.dragMode = "pan";
3982
+ /**
3983
+ * Rotation pivot behavior:
3984
+ * - 'center': Rotate around viewport center (default)
3985
+ * - '2d': Rotate around pointer position at ground level (z=0)
3986
+ * - '3d': Rotate around 3D picked point (requires pickPosition callback)
3987
+ */
3988
+ this.rotationPivot = "center";
3989
+ /** Add altitude to rotateStart params based on rotationPivot mode */
3990
+ this._getAltitude = (pos) => {
3991
+ if (this.rotationPivot === "2d") {
3992
+ return 0;
3993
+ } else if (this.rotationPivot === "3d") {
3994
+ if (this.pickPosition) {
3995
+ const { x, y } = this.props;
3996
+ const pickResult = this.pickPosition(x + pos[0], y + pos[1]);
3997
+ if (pickResult && pickResult.coordinate && pickResult.coordinate.length >= 3) {
3998
+ return pickResult.coordinate[2];
3999
+ }
4000
+ }
4001
+ }
4002
+ return void 0;
4003
+ };
4004
+ }
4005
+ setProps(props) {
4006
+ if ("rotationPivot" in props) {
4007
+ this.rotationPivot = props.rotationPivot || "center";
4008
+ }
4009
+ props.getAltitude = this._getAltitude;
4010
+ props.position = props.position || [0, 0, 0];
4011
+ props.maxBounds = props.maxBounds || (props.normalize === false ? null : WEB_MERCATOR_MAX_BOUNDS);
4012
+ super.setProps(props);
4013
+ }
4014
+ updateViewport(newControllerState, extraProps = null, interactionState = {}) {
4015
+ const state = newControllerState.getState();
4016
+ if (interactionState.isDragging && state.startRotateLngLat) {
4017
+ interactionState = {
4018
+ ...interactionState,
4019
+ rotationPivotPosition: state.startRotateLngLat
4020
+ };
4021
+ } else if (interactionState.isDragging === false) {
4022
+ interactionState = { ...interactionState, rotationPivotPosition: void 0 };
4023
+ }
4024
+ super.updateViewport(newControllerState, extraProps, interactionState);
4025
+ }
4026
+ };
4027
+
4028
+ // ../core/src/views/map-view.ts
4029
+ var MapView = class extends View {
4030
+ constructor(props = {}) {
4031
+ super(props);
4032
+ }
4033
+ getViewportType() {
4034
+ return WebMercatorViewport;
4035
+ }
4036
+ get ControllerType() {
4037
+ return MapController;
4038
+ }
4039
+ };
4040
+ MapView.displayName = "MapView";
4041
+
4042
+ // ../core/src/passes/draw-layers-pass.ts
4043
+ var DrawLayersPass = class extends LayersPass {
4044
+ shouldDrawLayer(layer) {
4045
+ const { operation } = layer.props;
4046
+ return operation.includes("draw") || operation.includes("terrain");
4047
+ }
4048
+ render(options) {
4049
+ return this._render(options);
4050
+ }
4051
+ };
4052
+
4053
+ // ../core/src/lib/deck-renderer.ts
4054
+ var TRACE_RENDER_LAYERS = "deckRenderer.renderLayers";
4055
+ var DeckRenderer = class {
4056
+ constructor(device2, opts = {}) {
4057
+ this.device = device2;
4058
+ this.stats = opts.stats;
4059
+ this.layerFilter = null;
4060
+ this.drawPickingColors = false;
4061
+ this.drawLayersPass = new DrawLayersPass(device2);
4062
+ this.pickLayersPass = new PickLayersPass(device2);
4063
+ this.renderCount = 0;
4064
+ this._needsRedraw = "Initial render";
4065
+ this.renderBuffers = [];
4066
+ this.lastPostProcessEffect = null;
4067
+ }
4068
+ setProps(props) {
4069
+ if (this.layerFilter !== props.layerFilter) {
4070
+ this.layerFilter = props.layerFilter;
4071
+ this._needsRedraw = "layerFilter changed";
4072
+ }
4073
+ if (this.drawPickingColors !== props.drawPickingColors) {
4074
+ this.drawPickingColors = props.drawPickingColors;
4075
+ this._needsRedraw = "drawPickingColors changed";
4076
+ }
4077
+ }
4078
+ renderLayers(opts) {
4079
+ const layerPass = this.drawPickingColors ? this.pickLayersPass : this.drawLayersPass;
4080
+ const renderOpts = {
4081
+ layerFilter: this.layerFilter,
4082
+ isPicking: this.drawPickingColors,
4083
+ ...opts
4084
+ };
4085
+ if (!opts.viewports.length) {
4086
+ const renderResult2 = layerPass.render(renderOpts);
4087
+ const renderStats2 = "stats" in renderResult2 ? renderResult2.stats : renderResult2;
4088
+ this._updateStats(renderStats2);
4089
+ return;
4090
+ }
4091
+ if (renderOpts.effects) {
4092
+ this._preRender(renderOpts.effects, renderOpts);
4093
+ }
4094
+ const outputBuffer = this.lastPostProcessEffect ? this.renderBuffers[0] : renderOpts.target;
4095
+ if (this.lastPostProcessEffect) {
4096
+ renderOpts.clearColor = [0, 0, 0, 0];
4097
+ renderOpts.clearCanvas = true;
4098
+ }
4099
+ const renderResult = layerPass.render({ ...renderOpts, target: outputBuffer });
4100
+ const renderStats = "stats" in renderResult ? renderResult.stats : renderResult;
4101
+ if (renderOpts.effects) {
4102
+ if (this.lastPostProcessEffect) {
4103
+ renderOpts.clearCanvas = opts.clearCanvas === void 0 ? true : opts.clearCanvas;
4104
+ }
4105
+ this._postRender(renderOpts.effects, renderOpts);
4106
+ }
4107
+ this.renderCount++;
4108
+ debug(TRACE_RENDER_LAYERS, this, renderStats, opts);
4109
+ this._updateStats(renderStats);
4110
+ }
4111
+ needsRedraw(opts = { clearRedrawFlags: false }) {
4112
+ const redraw = this._needsRedraw;
4113
+ if (opts.clearRedrawFlags) {
4114
+ this._needsRedraw = false;
4115
+ }
4116
+ return redraw;
4117
+ }
4118
+ finalize() {
4119
+ const { renderBuffers } = this;
4120
+ for (const buffer of renderBuffers) {
4121
+ buffer.delete();
4122
+ }
4123
+ renderBuffers.length = 0;
4124
+ }
4125
+ _updateStats(source2) {
4126
+ if (!this.stats) return;
4127
+ let layersCount = 0;
4128
+ for (const { visibleCount } of source2) {
4129
+ layersCount += visibleCount;
4130
+ }
4131
+ this.stats.get("Layers rendered").addCount(layersCount);
4132
+ }
4133
+ _preRender(effects, opts) {
4134
+ this.lastPostProcessEffect = null;
4135
+ opts.preRenderStats = opts.preRenderStats || {};
4136
+ for (const effect of effects) {
4137
+ opts.preRenderStats[effect.id] = effect.preRender(opts);
4138
+ if (effect.postRender) {
4139
+ this.lastPostProcessEffect = effect.id;
4140
+ }
4141
+ }
4142
+ if (this.lastPostProcessEffect) {
4143
+ this._resizeRenderBuffers(opts.canvasContext);
4144
+ }
4145
+ }
4146
+ _resizeRenderBuffers(canvasContext = this.device.canvasContext) {
4147
+ const { renderBuffers } = this;
4148
+ const size = canvasContext.getDrawingBufferSize();
4149
+ const [width, height] = size;
4150
+ if (renderBuffers.length === 0) {
4151
+ [0, 1].map((i) => {
4152
+ const texture = this.device.createTexture({
4153
+ sampler: { minFilter: "linear", magFilter: "linear" },
4154
+ width,
4155
+ height
4156
+ });
4157
+ renderBuffers.push(
4158
+ this.device.createFramebuffer({
4159
+ id: `deck-renderbuffer-${i}`,
4160
+ colorAttachments: [texture]
4161
+ })
4162
+ );
4163
+ });
4164
+ }
4165
+ for (const buffer of renderBuffers) {
4166
+ buffer.resize(size);
4167
+ }
4168
+ }
4169
+ _postRender(effects, opts) {
4170
+ var _a2;
4171
+ const { renderBuffers } = this;
4172
+ const target = opts.target ?? ((_a2 = opts.canvasContext) == null ? void 0 : _a2.getCurrentFramebuffer()) ?? opts.target;
4173
+ const params = {
4174
+ ...opts,
4175
+ inputBuffer: renderBuffers[0],
4176
+ swapBuffer: renderBuffers[1]
4177
+ };
4178
+ for (const effect of effects) {
4179
+ if (effect.postRender) {
4180
+ params.target = effect.id === this.lastPostProcessEffect ? target : void 0;
4181
+ const buffer = effect.postRender(params);
4182
+ params.inputBuffer = buffer;
4183
+ params.swapBuffer = buffer === renderBuffers[0] ? renderBuffers[1] : renderBuffers[0];
4184
+ }
4185
+ }
4186
+ }
4187
+ };
4188
+
4189
+ // ../core/src/utils/count.ts
4190
+ var ERR_NOT_OBJECT = "count(): argument not an object";
4191
+ var ERR_NOT_CONTAINER = "count(): argument not a container";
4192
+ function count(container) {
4193
+ if (!isObject(container)) {
4194
+ throw new Error(ERR_NOT_OBJECT);
4195
+ }
4196
+ if (typeof container.count === "function") {
4197
+ return container.count();
4198
+ }
4199
+ if (Number.isFinite(container.size)) {
4200
+ return container.size;
4201
+ }
4202
+ if (Number.isFinite(container.length)) {
4203
+ return container.length;
4204
+ }
4205
+ if (isPlainObject(container)) {
4206
+ return Object.keys(container).length;
4207
+ }
4208
+ throw new Error(ERR_NOT_CONTAINER);
4209
+ }
4210
+ function isPlainObject(value) {
4211
+ return value !== null && typeof value === "object" && value.constructor === Object;
4212
+ }
4213
+ function isObject(value) {
4214
+ return value !== null && typeof value === "object";
4215
+ }
38
4216
 
39
4217
  // dist/utils/setup-gl.js
40
4218
  var import_test_utils = require("@luma.gl/test-utils");
@@ -44,7 +4222,7 @@ var gl = ((_a = import_test_utils.webglDevice) == null ? void 0 : _a.gl) || 1;
44
4222
  globalThis.glContext = globalThis.glContext || gl;
45
4223
 
46
4224
  // dist/lifecycle-test.js
47
- var testViewport = new import_core.MapView({}).makeViewport({
4225
+ var testViewport = new MapView({}).makeViewport({
48
4226
  width: 100,
49
4227
  height: 100,
50
4228
  viewState: { longitude: 0, latitude: 0, zoom: 1 }
@@ -55,7 +4233,7 @@ function defaultOnError(error, title) {
55
4233
  }
56
4234
  }
57
4235
  function initializeLayerManager({ layer, viewport = testViewport, onError = defaultOnError }) {
58
- const layerManager = new import_core.LayerManager(device, { viewport });
4236
+ const layerManager = new LayerManager(device, { viewport });
59
4237
  layerManager.setProps({
60
4238
  onError: (error) => onError(error, `initializing ${layer.id}`)
61
4239
  });
@@ -74,7 +4252,7 @@ function testInitializeLayer(opts) {
74
4252
  }
75
4253
  async function testInitializeLayerAsync(opts) {
76
4254
  const layerManager = initializeLayerManager(opts);
77
- const deckRenderer = new import_core.DeckRenderer(device);
4255
+ const deckRenderer = new DeckRenderer(device);
78
4256
  while (!opts.layer.isLoaded) {
79
4257
  await update({ layerManager, deckRenderer, oldResourceCounts: {} });
80
4258
  }
@@ -124,8 +4302,8 @@ async function testLayerAsync(opts) {
124
4302
  }
125
4303
  function setupLayerTests(testTitle, { viewport = testViewport, timeline, onError = defaultOnError }) {
126
4304
  const oldResourceCounts = getResourceCounts();
127
- const layerManager = new import_core.LayerManager(device, { viewport, timeline });
128
- const deckRenderer = new import_core.DeckRenderer(device);
4305
+ const layerManager = new LayerManager(device, { viewport, timeline });
4306
+ const deckRenderer = new DeckRenderer(device);
129
4307
  const props = {
130
4308
  layerFilter: null,
131
4309
  drawPickingColors: false,
@@ -267,8 +4445,7 @@ function toLowPrecision(input, precision = 11) {
267
4445
  }
268
4446
 
269
4447
  // dist/generate-layer-tests.js
270
- var import_core2 = require("@deck.gl/core");
271
- function noop() {
4448
+ function noop2() {
272
4449
  }
273
4450
  function defaultAssert(condition, comment) {
274
4451
  if (!condition) {
@@ -279,12 +4456,12 @@ function generateLayerTests({
279
4456
  // eslint-disable-next-line @typescript-eslint/no-shadow
280
4457
  Layer,
281
4458
  sampleProps = {},
282
- assert = defaultAssert,
283
- onBeforeUpdate = noop,
284
- onAfterUpdate = noop,
4459
+ assert: assert2 = defaultAssert,
4460
+ onBeforeUpdate = noop2,
4461
+ onAfterUpdate = noop2,
285
4462
  runDefaultAsserts = true
286
4463
  }) {
287
- assert(Layer.layerName, "Layer should have display name");
4464
+ assert2(Layer.layerName, "Layer should have display name");
288
4465
  function wrapTestCaseTitle(title) {
289
4466
  return `${Layer.layerName}#${title}`;
290
4467
  }
@@ -306,20 +4483,20 @@ function generateLayerTests({
306
4483
  try {
307
4484
  new Layer({});
308
4485
  } catch (error) {
309
- assert(false, `Construct ${Layer.layerName} throws: ${error.message}`);
4486
+ assert2(false, `Construct ${Layer.layerName} throws: ${error.message}`);
310
4487
  }
311
4488
  const { _propTypes: propTypes, _mergedDefaultProps: defaultProps } = Layer;
312
4489
  testCases.push(...makeAltDataTestCases(sampleProps, propTypes));
313
4490
  for (const propName in Layer.defaultProps) {
314
4491
  if (!(propName in sampleProps)) {
315
- const newTestCase = makeAltPropTestCase({ propName, propTypes, defaultProps, sampleProps, assert }) || [];
4492
+ const newTestCase = makeAltPropTestCase({ propName, propTypes, defaultProps, sampleProps, assert: assert2 }) || [];
316
4493
  testCases.push(...newTestCase);
317
4494
  }
318
4495
  }
319
4496
  testCases.forEach((testCase) => {
320
4497
  testCase.title = wrapTestCaseTitle(testCase.title);
321
- const beforeFunc = testCase.onBeforeUpdate || noop;
322
- const afterFunc = testCase.onAfterUpdate || noop;
4498
+ const beforeFunc = testCase.onBeforeUpdate || noop2;
4499
+ const afterFunc = testCase.onAfterUpdate || noop2;
323
4500
  testCase.onBeforeUpdate = (params) => {
324
4501
  beforeFunc(params);
325
4502
  onBeforeUpdate(params);
@@ -330,18 +4507,18 @@ function generateLayerTests({
330
4507
  if (runDefaultAsserts) {
331
4508
  if (params.layer.isComposite) {
332
4509
  const { data } = params.layer.props;
333
- if (data && typeof data === "object" && (0, import_core2._count)(data)) {
334
- assert(params.subLayers.length, "Layer should have sublayers");
4510
+ if (data && typeof data === "object" && count(data)) {
4511
+ assert2(params.subLayers.length, "Layer should have sublayers");
335
4512
  }
336
4513
  } else {
337
- assert(params.layer.getModels().length, "Layer should have models");
4514
+ assert2(params.layer.getModels().length, "Layer should have models");
338
4515
  }
339
4516
  }
340
4517
  };
341
4518
  });
342
4519
  return testCases;
343
4520
  }
344
- function makeAltPropTestCase({ propName, propTypes, defaultProps, sampleProps, assert }) {
4521
+ function makeAltPropTestCase({ propName, propTypes, defaultProps, sampleProps, assert: assert2 }) {
345
4522
  const newProps = { ...sampleProps };
346
4523
  const propDef = propTypes[propName];
347
4524
  if (!propDef) {
@@ -383,7 +4560,7 @@ function makeAltPropTestCase({ propName, propTypes, defaultProps, sampleProps, a
383
4560
  [propName]: "function"
384
4561
  };
385
4562
  const onBeforeUpdate = () => callCount = 0;
386
- const onAfterUpdate = () => assert(callCount > 0, "accessor function is called");
4563
+ const onAfterUpdate = () => assert2(callCount > 0, "accessor function is called");
387
4564
  return [
388
4565
  {
389
4566
  title: `${propName}: () => ${defaultProps[propName]}`,