@deck.gl/core 9.2.11 → 9.3.0-alpha.2

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.
Files changed (152) hide show
  1. package/debug.min.js +1 -1
  2. package/dist/controllers/controller.d.ts +10 -0
  3. package/dist/controllers/controller.d.ts.map +1 -1
  4. package/dist/controllers/controller.js +15 -0
  5. package/dist/controllers/controller.js.map +1 -1
  6. package/dist/controllers/first-person-controller.d.ts +3 -2
  7. package/dist/controllers/first-person-controller.d.ts.map +1 -1
  8. package/dist/controllers/first-person-controller.js +13 -5
  9. package/dist/controllers/first-person-controller.js.map +1 -1
  10. package/dist/controllers/globe-controller.d.ts +1 -0
  11. package/dist/controllers/globe-controller.d.ts.map +1 -1
  12. package/dist/controllers/globe-controller.js +66 -5
  13. package/dist/controllers/globe-controller.js.map +1 -1
  14. package/dist/controllers/map-controller.d.ts +21 -3
  15. package/dist/controllers/map-controller.d.ts.map +1 -1
  16. package/dist/controllers/map-controller.js +139 -25
  17. package/dist/controllers/map-controller.js.map +1 -1
  18. package/dist/controllers/orbit-controller.d.ts +12 -4
  19. package/dist/controllers/orbit-controller.d.ts.map +1 -1
  20. package/dist/controllers/orbit-controller.js +118 -10
  21. package/dist/controllers/orbit-controller.js.map +1 -1
  22. package/dist/controllers/orthographic-controller.d.ts +117 -9
  23. package/dist/controllers/orthographic-controller.d.ts.map +1 -1
  24. package/dist/controllers/orthographic-controller.js +302 -37
  25. package/dist/controllers/orthographic-controller.js.map +1 -1
  26. package/dist/controllers/view-state.d.ts +4 -1
  27. package/dist/controllers/view-state.d.ts.map +1 -1
  28. package/dist/controllers/view-state.js +2 -1
  29. package/dist/controllers/view-state.js.map +1 -1
  30. package/dist/debug/loggers.d.ts.map +1 -1
  31. package/dist/debug/loggers.js +1 -4
  32. package/dist/debug/loggers.js.map +1 -1
  33. package/dist/dist.dev.js +7585 -10714
  34. package/dist/effects/lighting/lighting-effect.d.ts +1 -0
  35. package/dist/effects/lighting/lighting-effect.d.ts.map +1 -1
  36. package/dist/effects/lighting/lighting-effect.js +14 -5
  37. package/dist/effects/lighting/lighting-effect.js.map +1 -1
  38. package/dist/index.cjs +812 -120
  39. package/dist/index.cjs.map +4 -4
  40. package/dist/lib/attribute/attribute-manager.d.ts.map +1 -1
  41. package/dist/lib/attribute/attribute-manager.js +2 -0
  42. package/dist/lib/attribute/attribute-manager.js.map +1 -1
  43. package/dist/lib/attribute/data-column.js +2 -2
  44. package/dist/lib/attribute/data-column.js.map +1 -1
  45. package/dist/lib/attribute/gl-utils.d.ts +1 -1
  46. package/dist/lib/attribute/gl-utils.d.ts.map +1 -1
  47. package/dist/lib/attribute/gl-utils.js +4 -0
  48. package/dist/lib/attribute/gl-utils.js.map +1 -1
  49. package/dist/lib/deck-picker.d.ts +14 -1
  50. package/dist/lib/deck-picker.d.ts.map +1 -1
  51. package/dist/lib/deck-picker.js +43 -11
  52. package/dist/lib/deck-picker.js.map +1 -1
  53. package/dist/lib/deck-renderer.d.ts +6 -1
  54. package/dist/lib/deck-renderer.d.ts.map +1 -1
  55. package/dist/lib/deck-renderer.js +14 -2
  56. package/dist/lib/deck-renderer.js.map +1 -1
  57. package/dist/lib/deck.d.ts +10 -0
  58. package/dist/lib/deck.d.ts.map +1 -1
  59. package/dist/lib/deck.js +29 -11
  60. package/dist/lib/deck.js.map +1 -1
  61. package/dist/lib/init.js +2 -2
  62. package/dist/lib/init.js.map +1 -1
  63. package/dist/lib/layer.d.ts.map +1 -1
  64. package/dist/lib/layer.js +7 -3
  65. package/dist/lib/layer.js.map +1 -1
  66. package/dist/lib/view-manager.d.ts +4 -0
  67. package/dist/lib/view-manager.d.ts.map +1 -1
  68. package/dist/lib/view-manager.js +6 -1
  69. package/dist/lib/view-manager.js.map +1 -1
  70. package/dist/lib/widget.d.ts +4 -0
  71. package/dist/lib/widget.d.ts.map +1 -1
  72. package/dist/lib/widget.js +11 -0
  73. package/dist/lib/widget.js.map +1 -1
  74. package/dist/passes/draw-layers-pass.d.ts +2 -0
  75. package/dist/passes/draw-layers-pass.d.ts.map +1 -1
  76. package/dist/passes/draw-layers-pass.js +3 -0
  77. package/dist/passes/draw-layers-pass.js.map +1 -1
  78. package/dist/passes/layers-pass.d.ts +2 -1
  79. package/dist/passes/layers-pass.d.ts.map +1 -1
  80. package/dist/passes/layers-pass.js +7 -3
  81. package/dist/passes/layers-pass.js.map +1 -1
  82. package/dist/passes/pick-layers-pass.d.ts +6 -3
  83. package/dist/passes/pick-layers-pass.d.ts.map +1 -1
  84. package/dist/passes/pick-layers-pass.js +12 -4
  85. package/dist/passes/pick-layers-pass.js.map +1 -1
  86. package/dist/shaderlib/project/project.glsl.d.ts.map +1 -1
  87. package/dist/shaderlib/project/project.glsl.js +3 -0
  88. package/dist/shaderlib/project/project.glsl.js.map +1 -1
  89. package/dist/utils/deep-merge.d.ts +5 -0
  90. package/dist/utils/deep-merge.d.ts.map +1 -0
  91. package/dist/utils/deep-merge.js +31 -0
  92. package/dist/utils/deep-merge.js.map +1 -0
  93. package/dist/utils/math-utils.d.ts +4 -0
  94. package/dist/utils/math-utils.d.ts.map +1 -1
  95. package/dist/utils/math-utils.js +8 -0
  96. package/dist/utils/math-utils.js.map +1 -1
  97. package/dist/utils/texture.d.ts.map +1 -1
  98. package/dist/utils/texture.js +3 -1
  99. package/dist/utils/texture.js.map +1 -1
  100. package/dist/viewports/globe-viewport.d.ts +1 -0
  101. package/dist/viewports/globe-viewport.d.ts.map +1 -1
  102. package/dist/viewports/globe-viewport.js +1 -1
  103. package/dist/viewports/globe-viewport.js.map +1 -1
  104. package/dist/viewports/orbit-viewport.d.ts.map +1 -1
  105. package/dist/viewports/orbit-viewport.js +7 -2
  106. package/dist/viewports/orbit-viewport.js.map +1 -1
  107. package/dist/viewports/orthographic-viewport.d.ts +8 -2
  108. package/dist/viewports/orthographic-viewport.d.ts.map +1 -1
  109. package/dist/viewports/orthographic-viewport.js.map +1 -1
  110. package/dist/viewports/web-mercator-viewport.d.ts +5 -0
  111. package/dist/viewports/web-mercator-viewport.d.ts.map +1 -1
  112. package/dist/viewports/web-mercator-viewport.js +9 -0
  113. package/dist/viewports/web-mercator-viewport.js.map +1 -1
  114. package/dist/views/orthographic-view.d.ts +38 -4
  115. package/dist/views/orthographic-view.d.ts.map +1 -1
  116. package/dist/views/orthographic-view.js.map +1 -1
  117. package/dist/views/view.d.ts.map +1 -1
  118. package/dist/views/view.js +2 -8
  119. package/dist/views/view.js.map +1 -1
  120. package/dist.min.js +226 -154
  121. package/package.json +9 -9
  122. package/src/controllers/controller.ts +25 -2
  123. package/src/controllers/first-person-controller.ts +18 -8
  124. package/src/controllers/globe-controller.ts +89 -5
  125. package/src/controllers/map-controller.ts +174 -32
  126. package/src/controllers/orbit-controller.ts +147 -13
  127. package/src/controllers/orthographic-controller.ts +417 -41
  128. package/src/controllers/view-state.ts +10 -3
  129. package/src/debug/loggers.ts +1 -5
  130. package/src/effects/lighting/lighting-effect.ts +20 -8
  131. package/src/lib/attribute/attribute-manager.ts +1 -0
  132. package/src/lib/attribute/data-column.ts +3 -3
  133. package/src/lib/attribute/gl-utils.ts +5 -1
  134. package/src/lib/deck-picker.ts +47 -12
  135. package/src/lib/deck-renderer.ts +17 -3
  136. package/src/lib/deck.ts +39 -11
  137. package/src/lib/layer.ts +7 -3
  138. package/src/lib/view-manager.ts +9 -1
  139. package/src/lib/widget.ts +14 -0
  140. package/src/passes/draw-layers-pass.ts +5 -0
  141. package/src/passes/layers-pass.ts +9 -4
  142. package/src/passes/pick-layers-pass.ts +18 -6
  143. package/src/shaderlib/project/project.glsl.ts +3 -0
  144. package/src/utils/deep-merge.ts +33 -0
  145. package/src/utils/math-utils.ts +12 -0
  146. package/src/utils/texture.ts +3 -1
  147. package/src/viewports/globe-viewport.ts +1 -1
  148. package/src/viewports/orbit-viewport.ts +8 -2
  149. package/src/viewports/orthographic-viewport.ts +8 -2
  150. package/src/viewports/web-mercator-viewport.ts +10 -0
  151. package/src/views/orthographic-view.ts +38 -4
  152. package/src/views/view.ts +2 -8
package/dist/index.cjs CHANGED
@@ -183,15 +183,12 @@ var getLoggers = (log) => ({
183
183
  },
184
184
  /* Render events */
185
185
  "deckRenderer.renderLayers": (deckRenderer, renderStats, opts) => {
186
- const { pass, redrawReason, stats } = opts;
186
+ const { pass, redrawReason } = opts;
187
187
  for (const status of renderStats) {
188
188
  const { totalCount, visibleCount, compositeCount, pickableCount } = status;
189
189
  const primitiveCount = totalCount - compositeCount;
190
190
  const hiddenCount = primitiveCount - visibleCount;
191
191
  log.log(LOG_LEVEL_DRAW, `RENDER #${deckRenderer.renderCount} ${visibleCount} (of ${totalCount} layers) to ${pass} because ${redrawReason} (${hiddenCount} hidden, ${compositeCount} composite ${pickableCount} pickable)`)();
192
- if (stats) {
193
- stats.get("Redraw Layers").add(visibleCount);
194
- }
195
192
  }
196
193
  }
197
194
  });
@@ -232,7 +229,7 @@ var json_loader_default = {
232
229
 
233
230
  // dist/lib/init.js
234
231
  function checkVersion() {
235
- const version = true ? "9.2.11" : globalThis.DECK_VERSION || "untranspiled source";
232
+ const version = true ? "9.3.0-alpha.2" : globalThis.DECK_VERSION || "untranspiled source";
236
233
  const existingVersion = globalThis.deck && globalThis.deck.VERSION;
237
234
  if (existingVersion && existingVersion !== version) {
238
235
  throw new Error(`deck.gl - multiple versions detected: ${existingVersion} vs ${version}`);
@@ -1160,6 +1157,9 @@ return offset * project.focalDistance;
1160
1157
  float project_size_to_pixel(float meters) {
1161
1158
  return project_size(meters) * project.scale;
1162
1159
  }
1160
+ vec2 project_size_to_pixel(vec2 meters) {
1161
+ return project_size(meters) * project.scale;
1162
+ }
1163
1163
  float project_size_to_pixel(float size, int unit) {
1164
1164
  if (unit == UNIT_METERS) return project_size_to_pixel(size);
1165
1165
  if (unit == UNIT_COMMON) return size * project.scale;
@@ -1641,7 +1641,12 @@ var LayersPass = class extends Pass {
1641
1641
  this._lastRenderIndex = -1;
1642
1642
  }
1643
1643
  render(options) {
1644
- const [width, height] = this.device.canvasContext.getDrawingBufferSize();
1644
+ this._render(options);
1645
+ }
1646
+ _render(options) {
1647
+ const canvasContext = this.device.canvasContext;
1648
+ const framebuffer = options.target ?? canvasContext.getCurrentFramebuffer();
1649
+ const [width, height] = canvasContext.getDrawingBufferSize();
1645
1650
  const clearCanvas = options.clearCanvas ?? true;
1646
1651
  const clearColor = options.clearColor ?? (clearCanvas ? [0, 0, 0, 0] : false);
1647
1652
  const clearDepth = clearCanvas ? 1 : false;
@@ -1655,7 +1660,7 @@ var LayersPass = class extends Pass {
1655
1660
  parameters.scissorRect = options.scissorRect;
1656
1661
  }
1657
1662
  const renderPass = this.device.beginRenderPass({
1658
- framebuffer: options.target,
1663
+ framebuffer,
1659
1664
  parameters,
1660
1665
  clearColor,
1661
1666
  clearDepth,
@@ -2095,9 +2100,7 @@ var LightingEffect = class {
2095
2100
  } : {};
2096
2101
  const lightingProps = {
2097
2102
  enabled: true,
2098
- ambientLight: this.ambientLight,
2099
- directionalLights: this.directionalLights.map((directionalLight) => directionalLight.getProjectedLight({ layer })),
2100
- pointLights: this.pointLights.map((pointLight) => pointLight.getProjectedLight({ layer }))
2103
+ lights: this._getLights(layer)
2101
2104
  };
2102
2105
  const materialProps = layer.props.material;
2103
2106
  return {
@@ -2141,6 +2144,19 @@ var LightingEffect = class {
2141
2144
  this.directionalLights.push(new DirectionalLight(DEFAULT_DIRECTIONAL_LIGHT_PROPS[0]), new DirectionalLight(DEFAULT_DIRECTIONAL_LIGHT_PROPS[1]));
2142
2145
  }
2143
2146
  }
2147
+ _getLights(layer) {
2148
+ const lights = [];
2149
+ if (this.ambientLight) {
2150
+ lights.push(this.ambientLight);
2151
+ }
2152
+ for (const pointLight of this.pointLights) {
2153
+ lights.push(pointLight.getProjectedLight({ layer }));
2154
+ }
2155
+ for (const directionalLight of this.directionalLights) {
2156
+ lights.push(directionalLight.getProjectedLight({ layer }));
2157
+ }
2158
+ return lights;
2159
+ }
2144
2160
  };
2145
2161
 
2146
2162
  // dist/utils/typed-array-manager.js
@@ -2229,6 +2245,14 @@ function mod(value, divisor) {
2229
2245
  function getCameraPosition(viewMatrixInverse) {
2230
2246
  return [viewMatrixInverse[12], viewMatrixInverse[13], viewMatrixInverse[14]];
2231
2247
  }
2248
+ function getProjectionParameters(projectionMatrix) {
2249
+ const m22 = projectionMatrix[10];
2250
+ const m23 = projectionMatrix[14];
2251
+ return {
2252
+ near: m23 / (m22 - 1),
2253
+ far: m23 / (m22 + 1)
2254
+ };
2255
+ }
2232
2256
  function getFrustumPlanes(viewProjectionMatrix) {
2233
2257
  return {
2234
2258
  left: getFrustumPlane(viewProjectionMatrix[3] + viewProjectionMatrix[0], viewProjectionMatrix[7] + viewProjectionMatrix[4], viewProjectionMatrix[11] + viewProjectionMatrix[8], viewProjectionMatrix[15] + viewProjectionMatrix[12]),
@@ -2733,6 +2757,15 @@ var WebMercatorViewport = class extends viewport_default {
2733
2757
  const [longitude, latitude] = this.unprojectFlat(newCenter);
2734
2758
  return { longitude, latitude };
2735
2759
  }
2760
+ /**
2761
+ * Returns a new longitude and latitude that keeps a 3D world coordinate at a given screen pixel
2762
+ * This version handles the z-component (altitude) properly for cameras positioned above ground
2763
+ */
2764
+ panByPosition3D(coords, pixel) {
2765
+ const targetZ = coords[2] || 0;
2766
+ const deltaLngLat = import_core8.vec2.sub([], coords, this.unproject(pixel, { targetZ }));
2767
+ return { longitude: this.longitude + deltaLngLat[0], latitude: this.latitude + deltaLngLat[1] };
2768
+ }
2736
2769
  getBounds(options = {}) {
2737
2770
  const corners = (0, import_web_mercator3.getBounds)(this, options.z || 0);
2738
2771
  return [
@@ -3088,16 +3121,17 @@ var PickLayersPass = class extends LayersPass {
3088
3121
  if ("pickingFBO" in props) {
3089
3122
  return this._drawPickingBuffer(props);
3090
3123
  }
3091
- return super.render(props);
3124
+ const stats = super._render(props);
3125
+ return { decodePickingColor: null, stats };
3092
3126
  }
3093
3127
  // Private
3094
3128
  // Draws list of layers and viewports into the picking buffer
3095
3129
  // Note: does not sample the buffer, that has to be done by the caller
3096
- _drawPickingBuffer({ layers, layerFilter, views, viewports, onViewportActive, pickingFBO, deviceRect: { x, y, width, height }, cullRect, effects, pass = "picking", pickZ, shaderModuleProps }) {
3130
+ _drawPickingBuffer({ layers, layerFilter, views, viewports, onViewportActive, pickingFBO, deviceRect: { x, y, width, height }, cullRect, effects, pass = "picking", pickZ, shaderModuleProps, clearColor }) {
3097
3131
  this.pickZ = pickZ;
3098
3132
  const colorEncoderState = this._resetColorEncoder(pickZ);
3099
3133
  const scissorRect = [x, y, width, height];
3100
- const renderStatus = super.render({
3134
+ const renderStatus = super._render({
3101
3135
  target: pickingFBO,
3102
3136
  layers,
3103
3137
  layerFilter,
@@ -3109,7 +3143,7 @@ var PickLayersPass = class extends LayersPass {
3109
3143
  pass,
3110
3144
  isPicking: true,
3111
3145
  shaderModuleProps,
3112
- clearColor: [0, 0, 0, 0],
3146
+ clearColor: clearColor ?? [0, 0, 0, 0],
3113
3147
  colorMask: 15,
3114
3148
  scissorRect
3115
3149
  });
@@ -3131,6 +3165,7 @@ var PickLayersPass = class extends LayersPass {
3131
3165
  };
3132
3166
  }
3133
3167
  getLayerParameters(layer, layerIndex, viewport) {
3168
+ var _a;
3134
3169
  const pickParameters = {
3135
3170
  ...layer.props.parameters
3136
3171
  };
@@ -3141,6 +3176,9 @@ var PickLayersPass = class extends LayersPass {
3141
3176
  Object.assign(pickParameters, PICKING_BLENDING);
3142
3177
  pickParameters.blend = true;
3143
3178
  pickParameters.blendColor = encodeColor(this._colorEncoderState, layer, viewport);
3179
+ if (operation.includes("terrain") && ((_a = layer.state) == null ? void 0 : _a._hasPickingCover)) {
3180
+ pickParameters.blendAlphaSrcFactor = "one";
3181
+ }
3144
3182
  } else if (operation.includes("terrain")) {
3145
3183
  pickParameters.blend = false;
3146
3184
  }
@@ -3730,6 +3768,7 @@ var ViewManager = class {
3730
3768
  onViewStateChange: props.onViewStateChange,
3731
3769
  onInteractionStateChange: props.onInteractionStateChange
3732
3770
  };
3771
+ this._pickPosition = props.pickPosition;
3733
3772
  Object.seal(this);
3734
3773
  this.setProps(props);
3735
3774
  }
@@ -3838,6 +3877,9 @@ var ViewManager = class {
3838
3877
  if ("width" in props || "height" in props) {
3839
3878
  this._setSize(props.width, props.height);
3840
3879
  }
3880
+ if ("pickPosition" in props) {
3881
+ this._pickPosition = props.pickPosition;
3882
+ }
3841
3883
  if (!this._isUpdating) {
3842
3884
  this._update();
3843
3885
  }
@@ -3900,7 +3942,8 @@ var ViewManager = class {
3900
3942
  width: this.width,
3901
3943
  height: this.height
3902
3944
  });
3903
- }
3945
+ },
3946
+ pickPosition: this._pickPosition
3904
3947
  });
3905
3948
  return controller;
3906
3949
  }
@@ -4161,6 +4204,31 @@ function isAddSubSymbol(token) {
4161
4204
  return Boolean(token && token.type === "symbol" && (token.value === "+" || token.value === "-"));
4162
4205
  }
4163
4206
 
4207
+ // dist/utils/deep-merge.js
4208
+ function deepMergeViewState(a, b) {
4209
+ const result = { ...a };
4210
+ for (const key in b) {
4211
+ if (key === "id")
4212
+ continue;
4213
+ if (Array.isArray(result[key]) && Array.isArray(b[key])) {
4214
+ result[key] = mergeNumericArray(result[key], b[key]);
4215
+ } else {
4216
+ result[key] = b[key];
4217
+ }
4218
+ }
4219
+ return result;
4220
+ }
4221
+ function mergeNumericArray(target, source3) {
4222
+ target = target.slice();
4223
+ for (let i = 0; i < source3.length; i++) {
4224
+ const v = source3[i];
4225
+ if (Number.isFinite(v)) {
4226
+ target[i] = v;
4227
+ }
4228
+ }
4229
+ return target;
4230
+ }
4231
+
4164
4232
  // dist/views/view.js
4165
4233
  var View = class {
4166
4234
  constructor(props) {
@@ -4214,13 +4282,7 @@ var View = class {
4214
4282
  if (!this.props.viewState.id) {
4215
4283
  return this.props.viewState;
4216
4284
  }
4217
- const newViewState = { ...viewState };
4218
- for (const key in this.props.viewState) {
4219
- if (key !== "id") {
4220
- newViewState[key] = this.props.viewState[key];
4221
- }
4222
- }
4223
- return newViewState;
4285
+ return deepMergeViewState(viewState, this.props.viewState);
4224
4286
  }
4225
4287
  return viewState;
4226
4288
  }
@@ -4835,6 +4897,7 @@ var Controller = class {
4835
4897
  this.onStateChange = opts.onStateChange || (() => {
4836
4898
  });
4837
4899
  this.makeViewport = opts.makeViewport;
4900
+ this.pickPosition = opts.pickPosition;
4838
4901
  }
4839
4902
  set events(customEvents) {
4840
4903
  this.toggleEvents(this._customEvents, false);
@@ -4939,6 +5002,7 @@ var Controller = class {
4939
5002
  if (props.dragMode) {
4940
5003
  this.dragMode = props.dragMode;
4941
5004
  }
5005
+ const oldProps = this.props;
4942
5006
  this.props = props;
4943
5007
  if (!("transitionInterpolator" in props)) {
4944
5008
  props.transitionInterpolator = this._getTransitionProps().transitionInterpolator;
@@ -4961,6 +5025,15 @@ var Controller = class {
4961
5025
  this.touchZoom = touchZoom;
4962
5026
  this.touchRotate = touchRotate;
4963
5027
  this.keyboard = keyboard;
5028
+ const dimensionChanged = !oldProps || oldProps.height !== props.height || oldProps.width !== props.width || oldProps.maxBounds !== props.maxBounds;
5029
+ if (dimensionChanged && props.maxBounds) {
5030
+ const controllerState = new this.ControllerState({ ...props, makeViewport: this.makeViewport });
5031
+ const normalizedProps = controllerState.getViewportProps();
5032
+ const changed = Object.keys(normalizedProps).some((key) => !deepEqual(normalizedProps[key], props[key], 1));
5033
+ if (changed) {
5034
+ this.updateViewport(controllerState);
5035
+ }
5036
+ }
4964
5037
  }
4965
5038
  updateTransition() {
4966
5039
  this.transitionManager.updateTransition();
@@ -5366,7 +5439,8 @@ var Controller = class {
5366
5439
 
5367
5440
  // dist/controllers/view-state.js
5368
5441
  var ViewState = class {
5369
- constructor(props, state) {
5442
+ constructor(props, state, makeViewport) {
5443
+ this.makeViewport = makeViewport;
5370
5444
  this._viewportProps = this.applyConstraints(props);
5371
5445
  this._state = state;
5372
5446
  }
@@ -5382,6 +5456,22 @@ var ViewState = class {
5382
5456
  var import_web_mercator7 = require("@math.gl/web-mercator");
5383
5457
  var PITCH_MOUSE_THRESHOLD = 5;
5384
5458
  var PITCH_ACCEL = 1.2;
5459
+ var WEB_MERCATOR_TILE_SIZE = 512;
5460
+ var WEB_MERCATOR_MAX_BOUNDS = [
5461
+ [-Infinity, -90],
5462
+ [Infinity, 90]
5463
+ ];
5464
+ function lngLatToWorld2([lng, lat]) {
5465
+ if (Math.abs(lat) > 90) {
5466
+ lat = Math.sign(lat) * 90;
5467
+ }
5468
+ if (Number.isFinite(lng)) {
5469
+ const [x, y2] = (0, import_web_mercator7.lngLatToWorld)([lng, lat]);
5470
+ return [x, (0, import_core15.clamp)(y2, 0, WEB_MERCATOR_TILE_SIZE)];
5471
+ }
5472
+ const [, y] = (0, import_web_mercator7.lngLatToWorld)([0, lat]);
5473
+ return [lng, (0, import_core15.clamp)(y, 0, WEB_MERCATOR_TILE_SIZE)];
5474
+ }
5385
5475
  var MapState = class extends ViewState {
5386
5476
  constructor(options) {
5387
5477
  const {
@@ -5420,6 +5510,8 @@ var MapState = class extends ViewState {
5420
5510
  startZoomLngLat,
5421
5511
  /* Pointer position when rotation started */
5422
5512
  startRotatePos,
5513
+ /* The lng/lat point at the rotation pivot (where rotation started) */
5514
+ startRotateLngLat,
5423
5515
  /** Bearing when current perspective rotate operation started */
5424
5516
  startBearing,
5425
5517
  /** Pitch when current perspective rotate operation started */
@@ -5432,6 +5524,7 @@ var MapState = class extends ViewState {
5432
5524
  assert(Number.isFinite(longitude));
5433
5525
  assert(Number.isFinite(latitude));
5434
5526
  assert(Number.isFinite(zoom));
5527
+ const maxBounds = options.maxBounds || (normalize ? WEB_MERCATOR_MAX_BOUNDS : null);
5435
5528
  super({
5436
5529
  width,
5437
5530
  height,
@@ -5446,16 +5539,18 @@ var MapState = class extends ViewState {
5446
5539
  maxPitch,
5447
5540
  minPitch,
5448
5541
  normalize,
5449
- position
5542
+ position,
5543
+ maxBounds
5450
5544
  }, {
5451
5545
  startPanLngLat,
5452
5546
  startZoomLngLat,
5453
5547
  startRotatePos,
5548
+ startRotateLngLat,
5454
5549
  startBearing,
5455
5550
  startPitch,
5456
5551
  startZoom
5457
- });
5458
- this.makeViewport = options.makeViewport;
5552
+ }, options.makeViewport);
5553
+ this.getAltitude = options.getAltitude;
5459
5554
  }
5460
5555
  /**
5461
5556
  * Start panning
@@ -5495,8 +5590,11 @@ var MapState = class extends ViewState {
5495
5590
  * @param {[Number, Number]} pos - position on screen where the center is
5496
5591
  */
5497
5592
  rotateStart({ pos }) {
5593
+ var _a;
5594
+ const altitude = (_a = this.getAltitude) == null ? void 0 : _a.call(this, pos);
5498
5595
  return this._getUpdatedState({
5499
5596
  startRotatePos: pos,
5597
+ startRotateLngLat: altitude !== void 0 ? this._unproject3D(pos, altitude) : void 0,
5500
5598
  startBearing: this.getViewportProps().bearing,
5501
5599
  startPitch: this.getViewportProps().pitch
5502
5600
  });
@@ -5506,7 +5604,7 @@ var MapState = class extends ViewState {
5506
5604
  * @param {[Number, Number]} pos - position on screen where the center is
5507
5605
  */
5508
5606
  rotate({ pos, deltaAngleX = 0, deltaAngleY = 0 }) {
5509
- const { startRotatePos, startBearing, startPitch } = this.getState();
5607
+ const { startRotatePos, startRotateLngLat, startBearing, startPitch } = this.getState();
5510
5608
  if (!startRotatePos || startBearing === void 0 || startPitch === void 0) {
5511
5609
  return this;
5512
5610
  }
@@ -5519,6 +5617,17 @@ var MapState = class extends ViewState {
5519
5617
  pitch: startPitch + deltaAngleY
5520
5618
  };
5521
5619
  }
5620
+ if (startRotateLngLat) {
5621
+ const rotatedViewport = this.makeViewport({
5622
+ ...this.getViewportProps(),
5623
+ ...newRotation
5624
+ });
5625
+ const panMethod = "panByPosition3D" in rotatedViewport ? "panByPosition3D" : "panByPosition";
5626
+ return this._getUpdatedState({
5627
+ ...newRotation,
5628
+ ...rotatedViewport[panMethod](startRotateLngLat, startRotatePos)
5629
+ });
5630
+ }
5522
5631
  return this._getUpdatedState(newRotation);
5523
5632
  }
5524
5633
  /**
@@ -5527,6 +5636,8 @@ var MapState = class extends ViewState {
5527
5636
  */
5528
5637
  rotateEnd() {
5529
5638
  return this._getUpdatedState({
5639
+ startRotatePos: null,
5640
+ startRotateLngLat: null,
5530
5641
  startBearing: null,
5531
5642
  startPitch: null
5532
5643
  });
@@ -5558,9 +5669,7 @@ var MapState = class extends ViewState {
5558
5669
  if (!startZoomLngLat) {
5559
5670
  return this;
5560
5671
  }
5561
- const { maxZoom, minZoom } = this.getViewportProps();
5562
- let zoom = startZoom + Math.log2(scale);
5563
- zoom = (0, import_core15.clamp)(zoom, minZoom, maxZoom);
5672
+ const zoom = this._constrainZoom(startZoom + Math.log2(scale));
5564
5673
  const zoomedViewport = this.makeViewport({ ...this.getViewportProps(), zoom });
5565
5674
  return this._getUpdatedState({
5566
5675
  zoom,
@@ -5629,17 +5738,52 @@ var MapState = class extends ViewState {
5629
5738
  }
5630
5739
  // Apply any constraints (mathematical or defined by _viewportProps) to map state
5631
5740
  applyConstraints(props) {
5632
- const { maxZoom, minZoom, zoom } = props;
5633
- props.zoom = (0, import_core15.clamp)(zoom, minZoom, maxZoom);
5634
- const { maxPitch, minPitch, pitch } = props;
5635
- props.pitch = (0, import_core15.clamp)(pitch, minPitch, maxPitch);
5636
- const { normalize = true } = props;
5741
+ const { maxPitch, minPitch, pitch, longitude, bearing, normalize, maxBounds } = props;
5637
5742
  if (normalize) {
5638
- Object.assign(props, (0, import_web_mercator7.normalizeViewportProps)(props));
5743
+ if (longitude < -180 || longitude > 180) {
5744
+ props.longitude = mod(longitude + 180, 360) - 180;
5745
+ }
5746
+ if (bearing < -180 || bearing > 180) {
5747
+ props.bearing = mod(bearing + 180, 360) - 180;
5748
+ }
5749
+ }
5750
+ props.pitch = (0, import_core15.clamp)(pitch, minPitch, maxPitch);
5751
+ props.zoom = this._constrainZoom(props.zoom, props);
5752
+ if (maxBounds) {
5753
+ const bl = lngLatToWorld2(maxBounds[0]);
5754
+ const tr = lngLatToWorld2(maxBounds[1]);
5755
+ const scale = 2 ** props.zoom;
5756
+ const halfWidth = props.width / 2 / scale;
5757
+ const halfHeight = props.height / 2 / scale;
5758
+ const [minLng, minLat] = (0, import_web_mercator7.worldToLngLat)([bl[0] + halfWidth, bl[1] + halfHeight]);
5759
+ const [maxLng, maxLat] = (0, import_web_mercator7.worldToLngLat)([tr[0] - halfWidth, tr[1] - halfHeight]);
5760
+ props.longitude = (0, import_core15.clamp)(props.longitude, minLng, maxLng);
5761
+ props.latitude = (0, import_core15.clamp)(props.latitude, minLat, maxLat);
5639
5762
  }
5640
5763
  return props;
5641
5764
  }
5642
5765
  /* Private methods */
5766
+ _constrainZoom(zoom, props) {
5767
+ props || (props = this.getViewportProps());
5768
+ const { maxZoom, maxBounds } = props;
5769
+ const shouldApplyMaxBounds = maxBounds !== null && props.width > 0 && props.height > 0;
5770
+ let { minZoom } = props;
5771
+ if (shouldApplyMaxBounds) {
5772
+ const bl = lngLatToWorld2(maxBounds[0]);
5773
+ const tr = lngLatToWorld2(maxBounds[1]);
5774
+ const w = tr[0] - bl[0];
5775
+ const h = tr[1] - bl[1];
5776
+ if (Number.isFinite(w) && w > 0) {
5777
+ minZoom = Math.max(minZoom, Math.log2(props.width / w));
5778
+ }
5779
+ if (Number.isFinite(h) && h > 0) {
5780
+ minZoom = Math.max(minZoom, Math.log2(props.height / h));
5781
+ }
5782
+ if (minZoom > maxZoom)
5783
+ minZoom = maxZoom;
5784
+ }
5785
+ return (0, import_core15.clamp)(zoom, minZoom, maxZoom);
5786
+ }
5643
5787
  _zoomFromCenter(scale) {
5644
5788
  const { width, height } = this.getViewportProps();
5645
5789
  return this.zoom({
@@ -5666,6 +5810,10 @@ var MapState = class extends ViewState {
5666
5810
  const viewport = this.makeViewport(this.getViewportProps());
5667
5811
  return pos && viewport.unproject(pos);
5668
5812
  }
5813
+ _unproject3D(pos, altitude) {
5814
+ const viewport = this.makeViewport(this.getViewportProps());
5815
+ return viewport.unproject(pos, { targetZ: altitude });
5816
+ }
5669
5817
  _getNewRotation(pos, startPos, startPitch, startBearing) {
5670
5818
  const deltaX = pos[0] - startPos[0];
5671
5819
  const deltaY = pos[1] - startPos[1];
@@ -5712,19 +5860,42 @@ var MapController = class extends Controller {
5712
5860
  })
5713
5861
  };
5714
5862
  this.dragMode = "pan";
5863
+ this.rotationPivot = "center";
5864
+ this._getAltitude = (pos) => {
5865
+ if (this.rotationPivot === "2d") {
5866
+ return 0;
5867
+ } else if (this.rotationPivot === "3d") {
5868
+ if (this.pickPosition) {
5869
+ const { x, y } = this.props;
5870
+ const pickResult = this.pickPosition(x + pos[0], y + pos[1]);
5871
+ if (pickResult && pickResult.coordinate && pickResult.coordinate.length >= 3) {
5872
+ return pickResult.coordinate[2];
5873
+ }
5874
+ }
5875
+ }
5876
+ return void 0;
5877
+ };
5715
5878
  }
5716
5879
  setProps(props) {
5880
+ if ("rotationPivot" in props) {
5881
+ this.rotationPivot = props.rotationPivot || "center";
5882
+ }
5883
+ props.getAltitude = this._getAltitude;
5717
5884
  props.position = props.position || [0, 0, 0];
5718
- const oldProps = this.props;
5885
+ props.maxBounds = props.maxBounds || (props.normalize === false ? null : WEB_MERCATOR_MAX_BOUNDS);
5719
5886
  super.setProps(props);
5720
- const dimensionChanged = !oldProps || oldProps.height !== props.height;
5721
- if (dimensionChanged) {
5722
- this.updateViewport(new this.ControllerState({
5723
- makeViewport: this.makeViewport,
5724
- ...props,
5725
- ...this.state
5726
- }));
5887
+ }
5888
+ updateViewport(newControllerState, extraProps = null, interactionState = {}) {
5889
+ const state = newControllerState.getState();
5890
+ if (interactionState.isDragging && state.startRotateLngLat) {
5891
+ interactionState = {
5892
+ ...interactionState,
5893
+ rotationPivotPosition: state.startRotateLngLat
5894
+ };
5895
+ } else if (interactionState.isDragging === false) {
5896
+ interactionState = { ...interactionState, rotationPivotPosition: void 0 };
5727
5897
  }
5898
+ super.updateViewport(newControllerState, extraProps, interactionState);
5728
5899
  }
5729
5900
  };
5730
5901
 
@@ -5840,13 +6011,17 @@ var DrawLayersPass = class extends LayersPass {
5840
6011
  const { operation } = layer.props;
5841
6012
  return operation.includes("draw") || operation.includes("terrain");
5842
6013
  }
6014
+ render(options) {
6015
+ return this._render(options);
6016
+ }
5843
6017
  };
5844
6018
 
5845
6019
  // dist/lib/deck-renderer.js
5846
6020
  var TRACE_RENDER_LAYERS = "deckRenderer.renderLayers";
5847
6021
  var DeckRenderer = class {
5848
- constructor(device) {
6022
+ constructor(device, opts = {}) {
5849
6023
  this.device = device;
6024
+ this.stats = opts.stats;
5850
6025
  this.layerFilter = null;
5851
6026
  this.drawPickingColors = false;
5852
6027
  this.drawLayersPass = new DrawLayersPass(device);
@@ -5884,7 +6059,8 @@ var DeckRenderer = class {
5884
6059
  renderOpts.clearColor = [0, 0, 0, 0];
5885
6060
  renderOpts.clearCanvas = true;
5886
6061
  }
5887
- const renderStats = layerPass.render({ ...renderOpts, target: outputBuffer });
6062
+ const renderResult = layerPass.render({ ...renderOpts, target: outputBuffer });
6063
+ const renderStats = "stats" in renderResult ? renderResult.stats : renderResult;
5888
6064
  if (renderOpts.effects) {
5889
6065
  if (this.lastPostProcessEffect) {
5890
6066
  renderOpts.clearCanvas = opts.clearCanvas === void 0 ? true : opts.clearCanvas;
@@ -5893,6 +6069,7 @@ var DeckRenderer = class {
5893
6069
  }
5894
6070
  this.renderCount++;
5895
6071
  debug(TRACE_RENDER_LAYERS, this, renderStats, opts);
6072
+ this._updateStats(renderStats);
5896
6073
  }
5897
6074
  needsRedraw(opts = { clearRedrawFlags: false }) {
5898
6075
  const redraw = this._needsRedraw;
@@ -5908,6 +6085,15 @@ var DeckRenderer = class {
5908
6085
  }
5909
6086
  renderBuffers.length = 0;
5910
6087
  }
6088
+ _updateStats(source3) {
6089
+ if (!this.stats)
6090
+ return;
6091
+ let layersCount = 0;
6092
+ for (const { visibleCount } of source3) {
6093
+ layersCount += visibleCount;
6094
+ }
6095
+ this.stats.get("Layers rendered").addCount(layersCount);
6096
+ }
5911
6097
  _preRender(effects, opts) {
5912
6098
  this.lastPostProcessEffect = null;
5913
6099
  opts.preRenderStats = opts.preRenderStats || {};
@@ -6124,9 +6310,10 @@ function getViewportFromCoordinates(viewports, pixel) {
6124
6310
 
6125
6311
  // dist/lib/deck-picker.js
6126
6312
  var DeckPicker = class {
6127
- constructor(device) {
6313
+ constructor(device, opts = {}) {
6128
6314
  this._pickable = true;
6129
6315
  this.device = device;
6316
+ this.stats = opts.stats;
6130
6317
  this.pickLayersPass = new PickLayersPass(device);
6131
6318
  this.lastPickedInfo = {
6132
6319
  index: -1,
@@ -6289,15 +6476,16 @@ var DeckPicker = class {
6289
6476
  };
6290
6477
  }
6291
6478
  let z;
6292
- if (pickInfo.pickedLayer && unproject3D && this.depthFBO) {
6479
+ const depthLayers = this._getDepthLayers(pickInfo, pickableLayers, unproject3D);
6480
+ if (depthLayers.length > 0) {
6293
6481
  const { pickedColors: pickedColors2 } = this._drawAndSample({
6294
- layers: [pickInfo.pickedLayer],
6482
+ layers: depthLayers,
6295
6483
  views,
6296
6484
  viewports,
6297
6485
  onViewportActive,
6298
6486
  deviceRect: {
6299
- x: pickInfo.pickedX,
6300
- y: pickInfo.pickedY,
6487
+ x: pickInfo.pickedX ?? devicePixel[0],
6488
+ y: pickInfo.pickedY ?? devicePixel[1],
6301
6489
  width: 1,
6302
6490
  height: 1
6303
6491
  },
@@ -6403,15 +6591,16 @@ var DeckPicker = class {
6403
6591
  };
6404
6592
  }
6405
6593
  let z;
6406
- if (pickInfo.pickedLayer && unproject3D && this.depthFBO) {
6594
+ const depthLayers = this._getDepthLayers(pickInfo, pickableLayers, unproject3D);
6595
+ if (depthLayers.length > 0) {
6407
6596
  const { pickedColors: pickedColors2 } = this._drawAndSample({
6408
- layers: [pickInfo.pickedLayer],
6597
+ layers: depthLayers,
6409
6598
  views,
6410
6599
  viewports,
6411
6600
  onViewportActive,
6412
6601
  deviceRect: {
6413
- x: pickInfo.pickedX,
6414
- y: pickInfo.pickedY,
6602
+ x: pickInfo.pickedX ?? devicePixel[0],
6603
+ y: pickInfo.pickedY ?? devicePixel[1],
6415
6604
  width: 1,
6416
6605
  height: 1
6417
6606
  },
@@ -6608,7 +6797,8 @@ var DeckPicker = class {
6608
6797
  opts.preRenderStats[effect.id] = effect.preRender(opts);
6609
6798
  }
6610
6799
  }
6611
- const { decodePickingColor } = this.pickLayersPass.render(opts);
6800
+ const { decodePickingColor, stats } = this.pickLayersPass.render(opts);
6801
+ this._updateStats(stats);
6612
6802
  const { x, y, width, height } = deviceRect;
6613
6803
  const pickedColors = new (pickZ ? Float32Array : Uint8Array)(width * height * 4);
6614
6804
  this.device.readPixelsToArrayWebGL(pickingFBO, {
@@ -6643,7 +6833,8 @@ var DeckPicker = class {
6643
6833
  opts.preRenderStats[effect.id] = effect.preRender(opts);
6644
6834
  }
6645
6835
  }
6646
- const { decodePickingColor } = this.pickLayersPass.render(opts);
6836
+ const { decodePickingColor, stats } = this.pickLayersPass.render(opts);
6837
+ this._updateStats(stats);
6647
6838
  const { x, y, width, height } = deviceRect;
6648
6839
  const pickedColors = new (pickZ ? Float32Array : Uint8Array)(width * height * 4);
6649
6840
  this.device.readPixelsToArrayWebGL(pickingFBO, {
@@ -6655,6 +6846,33 @@ var DeckPicker = class {
6655
6846
  });
6656
6847
  return { pickedColors, decodePickingColor };
6657
6848
  }
6849
+ _updateStats(source3) {
6850
+ if (!this.stats)
6851
+ return;
6852
+ let layersCount = 0;
6853
+ for (const { visibleCount } of source3) {
6854
+ layersCount += visibleCount;
6855
+ }
6856
+ this.stats.get("Layers picked").addCount(layersCount);
6857
+ }
6858
+ /**
6859
+ * Determine which layers to use for the depth (pickZ) pass.
6860
+ * - If a non-draped layer was picked, use just that layer.
6861
+ * - If a draped layer was picked (geometry is at z=0) or no layer was picked
6862
+ * (e.g. no-FBO tiles at extreme zoom), fall back to terrain layers.
6863
+ */
6864
+ _getDepthLayers(pickInfo, pickableLayers, unproject3D) {
6865
+ var _a;
6866
+ if (!unproject3D || !this.depthFBO) {
6867
+ return [];
6868
+ }
6869
+ const { pickedLayer } = pickInfo;
6870
+ const isDraped = ((_a = pickedLayer == null ? void 0 : pickedLayer.state) == null ? void 0 : _a.terrainDrawMode) === "drape";
6871
+ if (pickedLayer && !isDraped) {
6872
+ return [pickedLayer];
6873
+ }
6874
+ return pickableLayers.filter((l) => l.props.operation.includes("terrain"));
6875
+ }
6658
6876
  /**
6659
6877
  * Calculate a picking rect centered on deviceX and deviceY and clipped to device
6660
6878
  * @returns null if pixel is outside of device
@@ -6939,6 +7157,17 @@ var Widget = class {
6939
7157
  this.onRenderHTML(this.rootElement);
6940
7158
  }
6941
7159
  }
7160
+ // VIEW STATE HELPERS
7161
+ /** Returns the current view state for the given view */
7162
+ getViewState(viewId) {
7163
+ var _a, _b;
7164
+ return ((_b = (_a = this.deck) == null ? void 0 : _a.viewManager) == null ? void 0 : _b.getViewState(viewId)) || {};
7165
+ }
7166
+ /** Updates the view state for the given view */
7167
+ setViewState(viewId, viewState) {
7168
+ var _a;
7169
+ (_a = this.deck) == null ? void 0 : _a._onViewStateChange({ viewId, viewState, interactionState: {} });
7170
+ }
6942
7171
  // @note empty method calls have an overhead in V8 but it is very low, ~1ns
6943
7172
  /**
6944
7173
  * Common utility to create the root DOM element for this widget
@@ -7165,10 +7394,15 @@ var Deck = class {
7165
7394
  this.metrics = {
7166
7395
  fps: 0,
7167
7396
  setPropsTime: 0,
7397
+ layersCount: 0,
7398
+ drawLayersCount: 0,
7399
+ updateLayersCount: 0,
7400
+ updateAttributesCount: 0,
7168
7401
  updateAttributesTime: 0,
7169
7402
  framesRedrawn: 0,
7170
7403
  pickTime: 0,
7171
7404
  pickCount: 0,
7405
+ pickLayersCount: 0,
7172
7406
  gpuTime: 0,
7173
7407
  gpuTimePerFrame: 0,
7174
7408
  cpuTime: 0,
@@ -7284,8 +7518,7 @@ var Deck = class {
7284
7518
  ...this.props.deviceProps,
7285
7519
  onResize: (canvasContext, info) => {
7286
7520
  const { width, height } = canvasContext.canvas;
7287
- canvasContext.drawingBufferWidth = width;
7288
- canvasContext.drawingBufferHeight = height;
7521
+ canvasContext.setDrawingBufferSize(width, height);
7289
7522
  this._needsRedraw = "Canvas resized";
7290
7523
  userOnResize == null ? void 0 : userOnResize(canvasContext, info);
7291
7524
  }
@@ -7459,6 +7692,14 @@ var Deck = class {
7459
7692
  pickObjects(opts) {
7460
7693
  return this._pick("pickObjects", "pickObjects Time", opts);
7461
7694
  }
7695
+ /**
7696
+ * Internal method used by controllers to pick 3D position at a screen coordinate
7697
+ * @private
7698
+ */
7699
+ _pickPositionForController(x, y) {
7700
+ const pickResult = this.pickObject({ x, y, radius: 0, unproject3D: true });
7701
+ return pickResult;
7702
+ }
7462
7703
  /** Experimental
7463
7704
  * Add a global resource for sharing among layers
7464
7705
  */
@@ -7683,7 +7924,7 @@ var Deck = class {
7683
7924
  }
7684
7925
  }
7685
7926
  _setDevice(device) {
7686
- var _a, _b;
7927
+ var _a, _b, _c, _d;
7687
7928
  this.device = device;
7688
7929
  if (!this.animationLoop) {
7689
7930
  return;
@@ -7710,7 +7951,8 @@ var Deck = class {
7710
7951
  const timeline = new import_engine3.Timeline();
7711
7952
  timeline.play();
7712
7953
  this.animationLoop.attachTimeline(timeline);
7713
- this.eventManager = new import_mjolnir2.EventManager(this.props.parent || this.canvas, {
7954
+ const eventRoot = ((_b = this.props.parent) == null ? void 0 : _b.querySelector(".deck-events-root")) || this.canvas;
7955
+ this.eventManager = new import_mjolnir2.EventManager(eventRoot, {
7714
7956
  touchAction: this.props.touchAction,
7715
7957
  recognizers: Object.keys(RECOGNIZERS).map((eventName) => {
7716
7958
  var _a2;
@@ -7737,6 +7979,7 @@ var Deck = class {
7737
7979
  eventManager: this.eventManager,
7738
7980
  onViewStateChange: this._onViewStateChange.bind(this),
7739
7981
  onInteractionStateChange: this._onInteractionStateChange.bind(this),
7982
+ pickPosition: this._pickPositionForController.bind(this),
7740
7983
  views: this._getViews(),
7741
7984
  viewState: this._getViewState(),
7742
7985
  width: this.width,
@@ -7753,11 +7996,12 @@ var Deck = class {
7753
7996
  deck: this,
7754
7997
  device: this.device
7755
7998
  });
7756
- this.deckRenderer = new DeckRenderer(this.device);
7757
- this.deckPicker = new DeckPicker(this.device);
7999
+ this.deckRenderer = new DeckRenderer(this.device, { stats: this.stats });
8000
+ this.deckPicker = new DeckPicker(this.device, { stats: this.stats });
8001
+ const widgetParent = ((_c = this.props.parent) == null ? void 0 : _c.querySelector(".deck-widgets-root")) || ((_d = this.canvas) == null ? void 0 : _d.parentElement);
7758
8002
  this.widgetManager = new WidgetManager({
7759
8003
  deck: this,
7760
- parentElement: (_b = this.canvas) == null ? void 0 : _b.parentElement
8004
+ parentElement: widgetParent
7761
8005
  });
7762
8006
  this.widgetManager.addDefault(new TooltipWidget());
7763
8007
  this.setProps(this.props);
@@ -7836,6 +8080,7 @@ var Deck = class {
7836
8080
  stats.get("CPU Time").addTime(animationLoopStats.get("CPU Time").lastTiming);
7837
8081
  }
7838
8082
  _getMetrics() {
8083
+ var _a;
7839
8084
  const { metrics, stats } = this;
7840
8085
  metrics.fps = stats.get("frameRate").getHz();
7841
8086
  metrics.setPropsTime = stats.get("setProps Time").time;
@@ -7843,11 +8088,16 @@ var Deck = class {
7843
8088
  metrics.framesRedrawn = stats.get("Redraw Count").count;
7844
8089
  metrics.pickTime = stats.get("pickObject Time").time + stats.get("pickMultipleObjects Time").time + stats.get("pickObjects Time").time;
7845
8090
  metrics.pickCount = stats.get("Pick Count").count;
8091
+ metrics.layersCount = ((_a = this.layerManager) == null ? void 0 : _a.layers.length) ?? 0;
8092
+ metrics.drawLayersCount = stats.get("Layers rendered").lastSampleCount;
8093
+ metrics.pickLayersCount = stats.get("Layers picked").lastSampleCount;
8094
+ metrics.updateAttributesCount = stats.get("Layers updated").count;
8095
+ metrics.updateAttributesCount = stats.get("Attributes updated").count;
7846
8096
  metrics.gpuTime = stats.get("GPU Time").time;
7847
8097
  metrics.cpuTime = stats.get("CPU Time").time;
7848
8098
  metrics.gpuTimePerFrame = stats.get("GPU Time").getAverageTime();
7849
8099
  metrics.cpuTimePerFrame = stats.get("CPU Time").getAverageTime();
7850
- const memoryStats = import_core16.luma.stats.get("Memory Usage");
8100
+ const memoryStats = import_core16.luma.stats.get("GPU Time and Memory");
7851
8101
  metrics.bufferMemory = memoryStats.get("Buffer Memory").count;
7852
8102
  metrics.textureMemory = memoryStats.get("Texture Memory").count;
7853
8103
  metrics.renderbufferMemory = memoryStats.get("Renderbuffer Memory").count;
@@ -7876,6 +8126,9 @@ function typedArrayFromDataType(type) {
7876
8126
  }
7877
8127
  var dataTypeFromTypedArray = import_core17.getDataType;
7878
8128
  function getBufferAttributeLayout(name, accessor, deviceType) {
8129
+ if (accessor.size > 4) {
8130
+ return null;
8131
+ }
7879
8132
  const type = deviceType === "webgpu" && accessor.type === "uint8" ? "unorm8" : accessor.type;
7880
8133
  return {
7881
8134
  attribute: name,
@@ -8028,8 +8281,7 @@ var DataColumn = class {
8028
8281
  const attributes = [];
8029
8282
  const result = {
8030
8283
  name: this.id,
8031
- byteStride: getStride(accessor),
8032
- attributes
8284
+ byteStride: getStride(accessor)
8033
8285
  };
8034
8286
  if (this.doublePrecision) {
8035
8287
  const doubleShaderAttributeDefs = resolveDoublePrecisionShaderAttributes(accessor, options || {});
@@ -8043,6 +8295,7 @@ var DataColumn = class {
8043
8295
  } else {
8044
8296
  attributes.push(getBufferAttributeLayout(attributeName, accessor, this.device.type));
8045
8297
  }
8298
+ result.attributes = attributes.filter(Boolean);
8046
8299
  return result;
8047
8300
  }
8048
8301
  setAccessor(accessor) {
@@ -9527,6 +9780,8 @@ var AttributeManager = class {
9527
9780
  }
9528
9781
  if (this.stats) {
9529
9782
  this.stats.get("Update Attributes").timeEnd();
9783
+ if (updated)
9784
+ this.stats.get("Attributes updated").incrementCount();
9530
9785
  }
9531
9786
  this.attributeTransitionManager.update({
9532
9787
  attributes: this.attributes,
@@ -10039,7 +10294,9 @@ function createTexture(owner, device, image, sampler) {
10039
10294
  },
10040
10295
  mipLevels: device.getMipLevelCount(width, height)
10041
10296
  });
10042
- texture.generateMipmapsWebGL();
10297
+ if (device.type === "webgl") {
10298
+ texture.generateMipmapsWebGL();
10299
+ }
10043
10300
  internalTextures[texture.id] = owner;
10044
10301
  return texture;
10045
10302
  }
@@ -10678,15 +10935,19 @@ var defaultProps2 = {
10678
10935
  fetch: {
10679
10936
  type: "function",
10680
10937
  value: (url, { propName, layer, loaders, loadOptions, signal }) => {
10938
+ var _a;
10681
10939
  const { resourceManager } = layer.context;
10682
10940
  loadOptions = loadOptions || layer.getLoadOptions();
10683
10941
  loaders = loaders || layer.props.loaders;
10684
10942
  if (signal) {
10685
10943
  loadOptions = {
10686
10944
  ...loadOptions,
10687
- fetch: {
10688
- ...loadOptions == null ? void 0 : loadOptions.fetch,
10689
- signal
10945
+ core: {
10946
+ ...loadOptions == null ? void 0 : loadOptions.core,
10947
+ fetch: {
10948
+ ...(_a = loadOptions == null ? void 0 : loadOptions.core) == null ? void 0 : _a.fetch,
10949
+ signal
10950
+ }
10690
10951
  }
10691
10952
  };
10692
10953
  }
@@ -10699,8 +10960,8 @@ var defaultProps2 = {
10699
10960
  return resourceManager.subscribe({
10700
10961
  resourceId: url,
10701
10962
  onChange: (data) => {
10702
- var _a;
10703
- return (_a = layer.internalState) == null ? void 0 : _a.reloadAsyncProp(propName, data);
10963
+ var _a2;
10964
+ return (_a2 = layer.internalState) == null ? void 0 : _a2.reloadAsyncProp(propName, data);
10704
10965
  },
10705
10966
  consumerId: layer.id,
10706
10967
  requestId: propName
@@ -11306,6 +11567,7 @@ var Layer = class extends component_default {
11306
11567
  if (!stateNeedsUpdate) {
11307
11568
  return;
11308
11569
  }
11570
+ this.context.stats.get("Layer updates").incrementCount();
11309
11571
  const currentProps = this.props;
11310
11572
  const context = this.context;
11311
11573
  const internalState = this.internalState;
@@ -11802,9 +12064,13 @@ var OrbitViewport = class extends viewport_default {
11802
12064
  }
11803
12065
  panByPosition(coords, pixel, startPixel) {
11804
12066
  const p0 = this.project(coords);
12067
+ const { near, far } = getProjectionParameters(this.projectionMatrix);
12068
+ const pz = near * far / (far - p0[2] * (far - near));
12069
+ const centerZ = near * far / (far - this.projectedCenter[2] * (far - near));
12070
+ const shiftScale = pz / centerZ;
11805
12071
  const nextCenter = [
11806
- this.width / 2 + p0[0] - pixel[0],
11807
- this.height / 2 + p0[1] - pixel[1],
12072
+ this.width / 2 + (p0[0] - pixel[0]) * shiftScale,
12073
+ this.height / 2 + (p0[1] - pixel[1]) * shiftScale,
11808
12074
  this.projectedCenter[2]
11809
12075
  ];
11810
12076
  return {
@@ -11955,6 +12221,7 @@ var FirstPersonState = class extends ViewState {
11955
12221
  latitude = null,
11956
12222
  maxPitch = 90,
11957
12223
  minPitch = -90,
12224
+ maxBounds = null,
11958
12225
  // Model state when the rotate operation first started
11959
12226
  startRotatePos,
11960
12227
  startBearing,
@@ -11972,7 +12239,8 @@ var FirstPersonState = class extends ViewState {
11972
12239
  longitude,
11973
12240
  latitude,
11974
12241
  maxPitch,
11975
- minPitch
12242
+ minPitch,
12243
+ maxBounds
11976
12244
  }, {
11977
12245
  startRotatePos,
11978
12246
  startBearing,
@@ -11980,8 +12248,7 @@ var FirstPersonState = class extends ViewState {
11980
12248
  startZoomPosition,
11981
12249
  startPanPos,
11982
12250
  startPanPosition
11983
- });
11984
- this.makeViewport = options.makeViewport;
12251
+ }, options.makeViewport);
11985
12252
  }
11986
12253
  /* Public API */
11987
12254
  /**
@@ -12191,7 +12458,7 @@ var FirstPersonState = class extends ViewState {
12191
12458
  }
12192
12459
  // Apply any constraints (mathematical or defined by _viewportProps) to map state
12193
12460
  applyConstraints(props) {
12194
- const { pitch, maxPitch, minPitch, longitude, bearing } = props;
12461
+ const { pitch, maxPitch, minPitch, longitude, position, bearing, maxBounds } = props;
12195
12462
  props.pitch = (0, import_core26.clamp)(pitch, minPitch, maxPitch);
12196
12463
  if (longitude !== null && (longitude < -180 || longitude > 180)) {
12197
12464
  props.longitude = mod(longitude + 180, 360) - 180;
@@ -12199,6 +12466,14 @@ var FirstPersonState = class extends ViewState {
12199
12466
  if (bearing < -180 || bearing > 180) {
12200
12467
  props.bearing = mod(bearing + 180, 360) - 180;
12201
12468
  }
12469
+ if (maxBounds) {
12470
+ const x = (0, import_core26.clamp)(position[0], maxBounds[0][0], maxBounds[1][0]);
12471
+ const y = (0, import_core26.clamp)(position[1], maxBounds[0][1], maxBounds[1][1]);
12472
+ const z = (0, import_core26.clamp)(position[2] ?? 0, maxBounds[0][2] ?? 0, maxBounds[1][2] ?? 0);
12473
+ if (x !== position[0] || y !== position[1] || z !== position[2]) {
12474
+ props.position = [x, y, z];
12475
+ }
12476
+ }
12202
12477
  return props;
12203
12478
  }
12204
12479
  };
@@ -12249,6 +12524,7 @@ var OrbitState = class extends ViewState {
12249
12524
  maxRotationX = 90,
12250
12525
  minZoom = -Infinity,
12251
12526
  maxZoom = Infinity,
12527
+ maxBounds = null,
12252
12528
  /** Interaction states, required to calculate change during transform */
12253
12529
  // Model state when the pan operation first started
12254
12530
  startPanPosition,
@@ -12270,7 +12546,8 @@ var OrbitState = class extends ViewState {
12270
12546
  minRotationX,
12271
12547
  maxRotationX,
12272
12548
  minZoom,
12273
- maxZoom
12549
+ maxZoom,
12550
+ maxBounds
12274
12551
  }, {
12275
12552
  startPanPosition,
12276
12553
  startRotatePos,
@@ -12278,8 +12555,8 @@ var OrbitState = class extends ViewState {
12278
12555
  startRotationOrbit,
12279
12556
  startZoomPosition,
12280
12557
  startZoom
12281
- });
12282
- this.makeViewport = options.makeViewport;
12558
+ }, options.makeViewport);
12559
+ this.unproject3D = options.unproject3D;
12283
12560
  }
12284
12561
  /**
12285
12562
  * Start panning
@@ -12464,17 +12741,20 @@ var OrbitState = class extends ViewState {
12464
12741
  return viewport.project(pos);
12465
12742
  }
12466
12743
  _unproject(pos) {
12744
+ var _a;
12745
+ const p = (_a = this.unproject3D) == null ? void 0 : _a.call(this, pos);
12746
+ if (p)
12747
+ return p;
12467
12748
  const viewport = this.makeViewport(this.getViewportProps());
12468
12749
  return viewport.unproject(pos);
12469
12750
  }
12470
12751
  // Calculates new zoom
12471
12752
  _calculateNewZoom({ scale, startZoom }) {
12472
- const { maxZoom, minZoom } = this.getViewportProps();
12473
12753
  if (startZoom === void 0) {
12474
12754
  startZoom = this.getViewportProps().zoom;
12475
12755
  }
12476
12756
  const zoom = startZoom + Math.log2(scale);
12477
- return (0, import_core27.clamp)(zoom, minZoom, maxZoom);
12757
+ return this._constrainZoom(zoom);
12478
12758
  }
12479
12759
  _panFromCenter(offset) {
12480
12760
  const { target } = this.getViewportProps();
@@ -12494,14 +12774,94 @@ var OrbitState = class extends ViewState {
12494
12774
  }
12495
12775
  // Apply any constraints (mathematical or defined by _viewportProps) to map state
12496
12776
  applyConstraints(props) {
12497
- const { maxZoom, minZoom, zoom, maxRotationX, minRotationX, rotationOrbit } = props;
12498
- props.zoom = Array.isArray(zoom) ? [(0, import_core27.clamp)(zoom[0], minZoom, maxZoom), (0, import_core27.clamp)(zoom[1], minZoom, maxZoom)] : (0, import_core27.clamp)(zoom, minZoom, maxZoom);
12777
+ const { maxRotationX, minRotationX, rotationOrbit } = props;
12778
+ props.zoom = this._constrainZoom(props.zoom, props);
12499
12779
  props.rotationX = (0, import_core27.clamp)(props.rotationX, minRotationX, maxRotationX);
12500
12780
  if (rotationOrbit < -180 || rotationOrbit > 180) {
12501
12781
  props.rotationOrbit = mod(rotationOrbit + 180, 360) - 180;
12502
12782
  }
12783
+ props.target = this._constrainTarget(props);
12503
12784
  return props;
12504
12785
  }
12786
+ _constrainZoom(zoom, props) {
12787
+ props || (props = this.getViewportProps());
12788
+ const { maxZoom, maxBounds } = props;
12789
+ let { minZoom } = props;
12790
+ if (maxBounds && props.width > 0 && props.height > 0) {
12791
+ const dx = maxBounds[1][0] - maxBounds[0][0];
12792
+ const dy = maxBounds[1][1] - maxBounds[0][1];
12793
+ const dz = (maxBounds[1][2] ?? 0) - (maxBounds[0][2] ?? 0);
12794
+ const maxDiameter = Math.sqrt(dx * dx + dy * dy + dz * dz);
12795
+ if (maxDiameter > 0) {
12796
+ minZoom = Math.max(minZoom, Math.log2(Math.min(props.width, props.height) / maxDiameter));
12797
+ if (minZoom > maxZoom)
12798
+ minZoom = maxZoom;
12799
+ }
12800
+ }
12801
+ return (0, import_core27.clamp)(zoom, minZoom, maxZoom);
12802
+ }
12803
+ _constrainTarget(props) {
12804
+ var _a;
12805
+ const { target, maxBounds } = props;
12806
+ if (!maxBounds)
12807
+ return target;
12808
+ const [[minX, minY, minZ = 0], [maxX, maxY, maxZ = 0]] = maxBounds;
12809
+ if (target[0] >= minX && target[0] <= maxX && target[1] >= minY && target[1] <= maxY && target[2] >= minZ && target[2] <= maxZ) {
12810
+ return target;
12811
+ }
12812
+ const vp = (_a = this.makeViewport) == null ? void 0 : _a.call(this, props);
12813
+ if (vp) {
12814
+ const { cameraPosition } = vp;
12815
+ const nx = cameraPosition[0] - target[0];
12816
+ const ny = cameraPosition[1] - target[1];
12817
+ const nz = cameraPosition[2] - target[2];
12818
+ const c = nx * target[0] + ny * target[1] + nz * target[2];
12819
+ const minDot = nx * (nx >= 0 ? minX : maxX) + ny * (ny >= 0 ? minY : maxY) + nz * (nz >= 0 ? minZ : maxZ);
12820
+ const maxDot = nx * (nx >= 0 ? maxX : minX) + ny * (ny >= 0 ? maxY : minY) + nz * (nz >= 0 ? maxZ : minZ);
12821
+ if ((nx || ny || nz) && c >= minDot && c <= maxDot) {
12822
+ const clampX = (value) => (0, import_core27.clamp)(value, minX, maxX);
12823
+ const clampY = (value) => (0, import_core27.clamp)(value, minY, maxY);
12824
+ const clampZ = (value) => (0, import_core27.clamp)(value, minZ, maxZ);
12825
+ const f = (lambda2) => nx * clampX(target[0] - lambda2 * nx) + ny * clampY(target[1] - lambda2 * ny) + nz * clampZ(target[2] - lambda2 * nz) - c;
12826
+ let lo = -1;
12827
+ let hi = 1;
12828
+ let flo = f(lo);
12829
+ let fhi = f(hi);
12830
+ while (flo < 0) {
12831
+ hi = lo;
12832
+ fhi = flo;
12833
+ lo *= 2;
12834
+ flo = f(lo);
12835
+ }
12836
+ while (fhi > 0) {
12837
+ lo = hi;
12838
+ flo = fhi;
12839
+ hi *= 2;
12840
+ fhi = f(hi);
12841
+ }
12842
+ for (let i = 0; i < 30; i++) {
12843
+ const mid = (lo + hi) / 2;
12844
+ const fm = f(mid);
12845
+ if (fm > 0) {
12846
+ lo = mid;
12847
+ } else {
12848
+ hi = mid;
12849
+ }
12850
+ }
12851
+ const lambda = (lo + hi) / 2;
12852
+ return [
12853
+ clampX(target[0] - lambda * nx),
12854
+ clampY(target[1] - lambda * ny),
12855
+ clampZ(target[2] - lambda * nz)
12856
+ ];
12857
+ }
12858
+ }
12859
+ return [
12860
+ (0, import_core27.clamp)(target[0], minX, maxX),
12861
+ (0, import_core27.clamp)(target[1], minY, maxY),
12862
+ (0, import_core27.clamp)(target[2], minZ, maxZ)
12863
+ ];
12864
+ }
12505
12865
  };
12506
12866
  var OrbitController = class extends Controller {
12507
12867
  constructor() {
@@ -12516,6 +12876,20 @@ var OrbitController = class extends Controller {
12516
12876
  }
12517
12877
  })
12518
12878
  };
12879
+ this._unproject3D = (pos) => {
12880
+ if (this.pickPosition) {
12881
+ const { x, y } = this.props;
12882
+ const pickResult = this.pickPosition(x + pos[0], y + pos[1]);
12883
+ if (pickResult && pickResult.coordinate) {
12884
+ return pickResult.coordinate;
12885
+ }
12886
+ }
12887
+ return null;
12888
+ };
12889
+ }
12890
+ setProps(props) {
12891
+ props.unproject3D = this._unproject3D;
12892
+ super.setProps(props);
12519
12893
  }
12520
12894
  };
12521
12895
 
@@ -12537,41 +12911,300 @@ var orbit_view_default = OrbitView;
12537
12911
 
12538
12912
  // dist/controllers/orthographic-controller.js
12539
12913
  var import_core28 = require("@math.gl/core");
12540
- var OrthographicState = class extends OrbitState {
12541
- constructor(props) {
12542
- super(props);
12543
- this.zoomAxis = props.zoomAxis || "all";
12914
+ function normalizeZoom({ zoom = 0, zoomX, zoomY }) {
12915
+ zoomX = zoomX ?? (Array.isArray(zoom) ? zoom[0] : zoom);
12916
+ zoomY = zoomY ?? (Array.isArray(zoom) ? zoom[1] : zoom);
12917
+ return { zoomX, zoomY };
12918
+ }
12919
+ var OrthographicState = class extends ViewState {
12920
+ constructor(options) {
12921
+ const {
12922
+ /* Viewport arguments */
12923
+ width,
12924
+ // Width of viewport
12925
+ height,
12926
+ // Height of viewport
12927
+ target = [0, 0, 0],
12928
+ zoom = 0,
12929
+ zoomAxis = "all",
12930
+ /* Viewport constraints */
12931
+ minZoom = -Infinity,
12932
+ maxZoom = Infinity,
12933
+ minZoomX = minZoom,
12934
+ maxZoomX = maxZoom,
12935
+ minZoomY = minZoom,
12936
+ maxZoomY = maxZoom,
12937
+ maxBounds = null,
12938
+ /** Interaction states, required to calculate change during transform */
12939
+ // Model state when the pan operation first started
12940
+ startPanPosition,
12941
+ // Model state when the zoom operation first started
12942
+ startZoomPosition,
12943
+ startZoom
12944
+ } = options;
12945
+ const { zoomX, zoomY } = normalizeZoom(options);
12946
+ super({
12947
+ width,
12948
+ height,
12949
+ target,
12950
+ zoom,
12951
+ zoomX,
12952
+ zoomY,
12953
+ zoomAxis,
12954
+ minZoomX,
12955
+ maxZoomX,
12956
+ minZoomY,
12957
+ maxZoomY,
12958
+ maxBounds
12959
+ }, {
12960
+ startPanPosition,
12961
+ startZoomPosition,
12962
+ startZoom
12963
+ }, options.makeViewport);
12964
+ }
12965
+ /**
12966
+ * Start panning
12967
+ * @param {[Number, Number]} pos - position on screen where the pointer grabs
12968
+ */
12969
+ panStart({ pos }) {
12970
+ return this._getUpdatedState({
12971
+ startPanPosition: this._unproject(pos)
12972
+ });
12973
+ }
12974
+ /**
12975
+ * Pan
12976
+ * @param {[Number, Number]} pos - position on screen where the pointer is
12977
+ */
12978
+ pan({ pos, startPosition }) {
12979
+ const startPanPosition = this.getState().startPanPosition || startPosition;
12980
+ if (!startPanPosition) {
12981
+ return this;
12982
+ }
12983
+ const viewport = this.makeViewport(this.getViewportProps());
12984
+ const newProps = viewport.panByPosition(startPanPosition, pos);
12985
+ return this._getUpdatedState(newProps);
12986
+ }
12987
+ /**
12988
+ * End panning
12989
+ * Must call if `panStart()` was called
12990
+ */
12991
+ panEnd() {
12992
+ return this._getUpdatedState({
12993
+ startPanPosition: null
12994
+ });
12995
+ }
12996
+ /**
12997
+ * Start rotating
12998
+ */
12999
+ rotateStart() {
13000
+ return this;
13001
+ }
13002
+ /**
13003
+ * Rotate
13004
+ */
13005
+ rotate() {
13006
+ return this;
13007
+ }
13008
+ /**
13009
+ * End rotating
13010
+ */
13011
+ rotateEnd() {
13012
+ return this;
13013
+ }
13014
+ // shortest path between two view states
13015
+ shortestPathFrom(viewState) {
13016
+ const fromProps = viewState.getViewportProps();
13017
+ const props = { ...this.getViewportProps() };
13018
+ return props;
13019
+ }
13020
+ /**
13021
+ * Start zooming
13022
+ * @param {[Number, Number]} pos - position on screen where the pointer grabs
13023
+ */
13024
+ zoomStart({ pos }) {
13025
+ const { zoomX, zoomY } = this.getViewportProps();
13026
+ return this._getUpdatedState({
13027
+ startZoomPosition: this._unproject(pos),
13028
+ startZoom: [zoomX, zoomY]
13029
+ });
13030
+ }
13031
+ /**
13032
+ * Zoom
13033
+ * @param {[Number, Number]} pos - position on screen where the current target is
13034
+ * @param {[Number, Number]} startPos - the target position at
13035
+ * the start of the operation. Must be supplied of `zoomStart()` was not called
13036
+ * @param {Number} scale - a number between [0, 1] specifying the accumulated
13037
+ * relative scale.
13038
+ */
13039
+ zoom({ pos, startPos, scale }) {
13040
+ let { startZoom, startZoomPosition } = this.getState();
13041
+ if (!startZoomPosition) {
13042
+ const { zoomX, zoomY } = this.getViewportProps();
13043
+ startZoom = [zoomX, zoomY];
13044
+ startZoomPosition = this._unproject(startPos || pos);
13045
+ }
13046
+ if (!startZoomPosition) {
13047
+ return this;
13048
+ }
13049
+ const newZoomProps = this._constrainZoom(this._calculateNewZoom({ scale, startZoom }));
13050
+ const zoomedViewport = this.makeViewport({ ...this.getViewportProps(), ...newZoomProps });
13051
+ return this._getUpdatedState({
13052
+ ...newZoomProps,
13053
+ ...zoomedViewport.panByPosition(startZoomPosition, pos)
13054
+ });
13055
+ }
13056
+ /**
13057
+ * End zooming
13058
+ * Must call if `zoomStart()` was called
13059
+ */
13060
+ zoomEnd() {
13061
+ return this._getUpdatedState({
13062
+ startZoomPosition: null,
13063
+ startZoom: null
13064
+ });
13065
+ }
13066
+ zoomIn(speed = 2) {
13067
+ return this._getUpdatedState(this._calculateNewZoom({ scale: speed }));
13068
+ }
13069
+ zoomOut(speed = 2) {
13070
+ return this._getUpdatedState(this._calculateNewZoom({ scale: 1 / speed }));
13071
+ }
13072
+ moveLeft(speed = 50) {
13073
+ return this._panFromCenter([-speed, 0]);
13074
+ }
13075
+ moveRight(speed = 50) {
13076
+ return this._panFromCenter([speed, 0]);
13077
+ }
13078
+ moveUp(speed = 50) {
13079
+ return this._panFromCenter([0, -speed]);
13080
+ }
13081
+ moveDown(speed = 50) {
13082
+ return this._panFromCenter([0, speed]);
12544
13083
  }
13084
+ rotateLeft(speed = 15) {
13085
+ return this;
13086
+ }
13087
+ rotateRight(speed = 15) {
13088
+ return this;
13089
+ }
13090
+ rotateUp(speed = 10) {
13091
+ return this;
13092
+ }
13093
+ rotateDown(speed = 10) {
13094
+ return this;
13095
+ }
13096
+ /* Private methods */
13097
+ _project(pos) {
13098
+ const viewport = this.makeViewport(this.getViewportProps());
13099
+ return viewport.project(pos);
13100
+ }
13101
+ _unproject(pos) {
13102
+ const viewport = this.makeViewport(this.getViewportProps());
13103
+ return viewport.unproject(pos);
13104
+ }
13105
+ // Calculates new zoom
12545
13106
  _calculateNewZoom({ scale, startZoom }) {
12546
- const { maxZoom, minZoom } = this.getViewportProps();
13107
+ const { zoomX, zoomY, zoomAxis } = this.getViewportProps();
12547
13108
  if (startZoom === void 0) {
12548
- startZoom = this.getViewportProps().zoom;
13109
+ startZoom = [zoomX, zoomY];
12549
13110
  }
12550
- let deltaZoom = Math.log2(scale);
12551
- if (Array.isArray(startZoom)) {
12552
- let [newZoomX, newZoomY] = startZoom;
12553
- switch (this.zoomAxis) {
12554
- case "X":
12555
- newZoomX = (0, import_core28.clamp)(newZoomX + deltaZoom, minZoom, maxZoom);
12556
- break;
12557
- case "Y":
12558
- newZoomY = (0, import_core28.clamp)(newZoomY + deltaZoom, minZoom, maxZoom);
12559
- break;
12560
- default:
12561
- let z = Math.min(newZoomX + deltaZoom, newZoomY + deltaZoom);
12562
- if (z < minZoom) {
12563
- deltaZoom += minZoom - z;
12564
- }
12565
- z = Math.max(newZoomX + deltaZoom, newZoomY + deltaZoom);
12566
- if (z > maxZoom) {
12567
- deltaZoom += maxZoom - z;
12568
- }
12569
- newZoomX += deltaZoom;
12570
- newZoomY += deltaZoom;
13111
+ const deltaZoom = Math.log2(scale);
13112
+ let [newZoomX, newZoomY] = startZoom;
13113
+ switch (zoomAxis) {
13114
+ case "X":
13115
+ newZoomX += deltaZoom;
13116
+ break;
13117
+ case "Y":
13118
+ newZoomY += deltaZoom;
13119
+ break;
13120
+ default:
13121
+ newZoomX += deltaZoom;
13122
+ newZoomY += deltaZoom;
13123
+ }
13124
+ return {
13125
+ zoomX: newZoomX,
13126
+ zoomY: newZoomY
13127
+ };
13128
+ }
13129
+ _panFromCenter(offset) {
13130
+ const { target } = this.getViewportProps();
13131
+ const center = this._project(target);
13132
+ return this.pan({
13133
+ startPosition: target,
13134
+ pos: [center[0] + offset[0], center[1] + offset[1]]
13135
+ });
13136
+ }
13137
+ _getUpdatedState(newProps) {
13138
+ return new this.constructor({
13139
+ makeViewport: this.makeViewport,
13140
+ ...this.getViewportProps(),
13141
+ ...this.getState(),
13142
+ ...newProps
13143
+ });
13144
+ }
13145
+ // Apply any constraints (mathematical or defined by _viewportProps) to map state
13146
+ applyConstraints(props) {
13147
+ const { zoomX, zoomY } = this._constrainZoom(props, props);
13148
+ props.zoomX = zoomX;
13149
+ props.zoomY = zoomY;
13150
+ props.zoom = Array.isArray(props.zoom) || props.zoomX !== props.zoomY ? [props.zoomX, props.zoomY] : props.zoomX;
13151
+ const { maxBounds, target } = props;
13152
+ if (maxBounds) {
13153
+ const halfWidth = props.width / 2 / 2 ** zoomX;
13154
+ const halfHeight = props.height / 2 / 2 ** zoomY;
13155
+ const minX = maxBounds[0][0] + halfWidth;
13156
+ const maxX = maxBounds[1][0] - halfWidth;
13157
+ const minY = maxBounds[0][1] + halfHeight;
13158
+ const maxY = maxBounds[1][1] - halfHeight;
13159
+ const x = (0, import_core28.clamp)(target[0], minX, maxX);
13160
+ const y = (0, import_core28.clamp)(target[1], minY, maxY);
13161
+ if (x !== target[0] || y !== target[1]) {
13162
+ props.target = target.slice();
13163
+ props.target[0] = x;
13164
+ props.target[1] = y;
12571
13165
  }
12572
- return [newZoomX, newZoomY];
12573
13166
  }
12574
- return (0, import_core28.clamp)(startZoom + deltaZoom, minZoom, maxZoom);
13167
+ return props;
13168
+ }
13169
+ _constrainZoom({ zoomX, zoomY }, props) {
13170
+ props || (props = this.getViewportProps());
13171
+ const { zoomAxis, maxZoomX, maxZoomY, maxBounds } = props;
13172
+ let { minZoomX, minZoomY } = props;
13173
+ const shouldApplyMaxBounds = maxBounds !== null && props.width > 0 && props.height > 0;
13174
+ if (shouldApplyMaxBounds) {
13175
+ const bl = maxBounds[0];
13176
+ const tr = maxBounds[1];
13177
+ const w = tr[0] - bl[0];
13178
+ const h = tr[1] - bl[1];
13179
+ if (Number.isFinite(w) && w > 0) {
13180
+ minZoomX = Math.max(minZoomX, Math.log2(props.width / w));
13181
+ if (minZoomX > maxZoomX)
13182
+ minZoomX = maxZoomX;
13183
+ }
13184
+ if (Number.isFinite(h) && h > 0) {
13185
+ minZoomY = Math.max(minZoomY, Math.log2(props.height / h));
13186
+ if (minZoomY > maxZoomY)
13187
+ minZoomY = maxZoomY;
13188
+ }
13189
+ }
13190
+ switch (zoomAxis) {
13191
+ case "X":
13192
+ zoomX = (0, import_core28.clamp)(zoomX, minZoomX, maxZoomX);
13193
+ break;
13194
+ case "Y":
13195
+ zoomY = (0, import_core28.clamp)(zoomY, minZoomY, maxZoomY);
13196
+ break;
13197
+ default:
13198
+ let delta = Math.min(maxZoomX - zoomX, maxZoomY - zoomY, 0);
13199
+ if (delta === 0) {
13200
+ delta = Math.max(minZoomX - zoomX, minZoomY - zoomY, 0);
13201
+ }
13202
+ if (delta !== 0) {
13203
+ zoomX += delta;
13204
+ zoomY += delta;
13205
+ }
13206
+ }
13207
+ return { zoomX, zoomY };
12575
13208
  }
12576
13209
  };
12577
13210
  var OrthographicController = class extends Controller {
@@ -12580,10 +13213,14 @@ var OrthographicController = class extends Controller {
12580
13213
  this.ControllerState = OrthographicState;
12581
13214
  this.transition = {
12582
13215
  transitionDuration: 300,
12583
- transitionInterpolator: new LinearInterpolator(["target", "zoom"])
13216
+ transitionInterpolator: new LinearInterpolator(["target", "zoomX", "zoomY"])
12584
13217
  };
12585
13218
  this.dragMode = "pan";
12586
13219
  }
13220
+ setProps(props) {
13221
+ Object.assign(props, normalizeZoom(props));
13222
+ super.setProps(props);
13223
+ }
12587
13224
  _onPanRotate() {
12588
13225
  return false;
12589
13226
  }
@@ -12607,9 +13244,22 @@ var orthographic_view_default = OrthographicView;
12607
13244
  // dist/controllers/globe-controller.js
12608
13245
  var import_core29 = require("@math.gl/core");
12609
13246
  var import_web_mercator12 = require("@math.gl/web-mercator");
13247
+ var DEGREES_TO_RADIANS4 = Math.PI / 180;
13248
+ var RADIANS_TO_DEGREES2 = 180 / Math.PI;
13249
+ function degreesToPixels(angle, zoom = 0) {
13250
+ const radians = Math.min(180, angle) * DEGREES_TO_RADIANS4;
13251
+ const size = GLOBE_RADIUS * 2 * Math.sin(radians / 2);
13252
+ return size * Math.pow(2, zoom);
13253
+ }
13254
+ function pixelsToDegrees(pixels, zoom = 0) {
13255
+ const size = pixels / Math.pow(2, zoom);
13256
+ const radians = Math.asin(Math.min(1, size / GLOBE_RADIUS / 2)) * 2;
13257
+ return radians * RADIANS_TO_DEGREES2;
13258
+ }
12610
13259
  var GlobeState = class extends MapState {
12611
13260
  constructor(options) {
12612
13261
  const { startPanPos, ...mapStateOptions } = options;
13262
+ mapStateOptions.normalize = false;
12613
13263
  super(mapStateOptions);
12614
13264
  if (startPanPos !== void 0) {
12615
13265
  this._state.startPanPos = startPanPos;
@@ -12648,16 +13298,58 @@ var GlobeState = class extends MapState {
12648
13298
  return this._getUpdatedState({ zoom });
12649
13299
  }
12650
13300
  applyConstraints(props) {
12651
- const { longitude, latitude, maxZoom, minZoom, zoom } = props;
12652
- const ZOOM0 = zoomAdjust(0);
12653
- const zoomAdjustment = zoomAdjust(latitude) - ZOOM0;
12654
- props.zoom = (0, import_core29.clamp)(zoom, minZoom + zoomAdjustment, maxZoom + zoomAdjustment);
13301
+ const { longitude, latitude, maxBounds } = props;
13302
+ props.zoom = this._constrainZoom(props.zoom, props);
12655
13303
  if (longitude < -180 || longitude > 180) {
12656
13304
  props.longitude = mod(longitude + 180, 360) - 180;
12657
13305
  }
12658
13306
  props.latitude = (0, import_core29.clamp)(latitude, -import_web_mercator12.MAX_LATITUDE, import_web_mercator12.MAX_LATITUDE);
13307
+ if (maxBounds) {
13308
+ props.longitude = (0, import_core29.clamp)(props.longitude, maxBounds[0][0], maxBounds[1][0]);
13309
+ props.latitude = (0, import_core29.clamp)(props.latitude, maxBounds[0][1], maxBounds[1][1]);
13310
+ }
13311
+ if (maxBounds) {
13312
+ const effectiveZoom = props.zoom - zoomAdjust(latitude);
13313
+ const lngSpan = maxBounds[1][0] - maxBounds[0][0];
13314
+ const latSpan = maxBounds[1][1] - maxBounds[0][1];
13315
+ if (latSpan > 0 && latSpan < import_web_mercator12.MAX_LATITUDE * 2) {
13316
+ const halfHeightDegrees = Math.min(pixelsToDegrees(props.height, effectiveZoom), latSpan) / 2;
13317
+ props.latitude = (0, import_core29.clamp)(props.latitude, maxBounds[0][1] + halfHeightDegrees, maxBounds[1][1] - halfHeightDegrees);
13318
+ }
13319
+ if (lngSpan > 0 && lngSpan < 360) {
13320
+ const halfWidthDegrees = Math.min(pixelsToDegrees(props.width / Math.cos(props.latitude * DEGREES_TO_RADIANS4), effectiveZoom), lngSpan) / 2;
13321
+ props.longitude = (0, import_core29.clamp)(props.longitude, maxBounds[0][0] + halfWidthDegrees, maxBounds[1][0] - halfWidthDegrees);
13322
+ }
13323
+ }
13324
+ if (props.latitude !== latitude) {
13325
+ props.zoom += zoomAdjust(props.latitude) - zoomAdjust(latitude);
13326
+ }
12659
13327
  return props;
12660
13328
  }
13329
+ _constrainZoom(zoom, props) {
13330
+ props || (props = this.getViewportProps());
13331
+ const { latitude, maxZoom, maxBounds } = props;
13332
+ let { minZoom } = props;
13333
+ const ZOOM0 = zoomAdjust(0);
13334
+ const zoomAdjustment = zoomAdjust(latitude) - ZOOM0;
13335
+ const shouldApplyMaxBounds = maxBounds !== null && props.width > 0 && props.height > 0;
13336
+ if (shouldApplyMaxBounds) {
13337
+ const minLatitude = maxBounds[0][1];
13338
+ const maxLatitude = maxBounds[1][1];
13339
+ const fitLatitude = Math.sign(minLatitude) === Math.sign(maxLatitude) ? Math.min(Math.abs(minLatitude), Math.abs(maxLatitude)) : 0;
13340
+ const w = degreesToPixels(maxBounds[1][0] - maxBounds[0][0]) * Math.cos(fitLatitude * DEGREES_TO_RADIANS4);
13341
+ const h = degreesToPixels(maxBounds[1][1] - maxBounds[0][1]);
13342
+ if (w > 0) {
13343
+ minZoom = Math.max(minZoom, Math.log2(props.width / w) + ZOOM0);
13344
+ }
13345
+ if (h > 0) {
13346
+ minZoom = Math.max(minZoom, Math.log2(props.height / h) + ZOOM0);
13347
+ }
13348
+ if (minZoom > maxZoom)
13349
+ minZoom = maxZoom;
13350
+ }
13351
+ return (0, import_core29.clamp)(zoom, minZoom + zoomAdjustment, maxZoom + zoomAdjustment);
13352
+ }
12661
13353
  };
12662
13354
  var GlobeController = class extends Controller {
12663
13355
  constructor() {