@deck.gl/core 9.3.0-alpha.1 → 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 (124) hide show
  1. package/debug.min.js +1 -1
  2. package/dist/controllers/controller.d.ts +5 -4
  3. package/dist/controllers/controller.d.ts.map +1 -1
  4. package/dist/controllers/controller.js +18 -7
  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 +7 -18
  15. package/dist/controllers/map-controller.d.ts.map +1 -1
  16. package/dist/controllers/map-controller.js +94 -50
  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 +2 -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 +3705 -1678
  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 +685 -123
  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/deck-picker.d.ts +6 -1
  44. package/dist/lib/deck-picker.d.ts.map +1 -1
  45. package/dist/lib/deck-picker.js +15 -3
  46. package/dist/lib/deck-picker.js.map +1 -1
  47. package/dist/lib/deck-renderer.d.ts +6 -1
  48. package/dist/lib/deck-renderer.d.ts.map +1 -1
  49. package/dist/lib/deck-renderer.js +14 -2
  50. package/dist/lib/deck-renderer.js.map +1 -1
  51. package/dist/lib/deck.d.ts +5 -0
  52. package/dist/lib/deck.d.ts.map +1 -1
  53. package/dist/lib/deck.js +13 -3
  54. package/dist/lib/deck.js.map +1 -1
  55. package/dist/lib/init.js +2 -2
  56. package/dist/lib/layer.d.ts.map +1 -1
  57. package/dist/lib/layer.js +1 -0
  58. package/dist/lib/layer.js.map +1 -1
  59. package/dist/passes/draw-layers-pass.d.ts +2 -0
  60. package/dist/passes/draw-layers-pass.d.ts.map +1 -1
  61. package/dist/passes/draw-layers-pass.js +3 -0
  62. package/dist/passes/draw-layers-pass.js.map +1 -1
  63. package/dist/passes/layers-pass.d.ts +2 -1
  64. package/dist/passes/layers-pass.d.ts.map +1 -1
  65. package/dist/passes/layers-pass.js +3 -0
  66. package/dist/passes/layers-pass.js.map +1 -1
  67. package/dist/passes/pick-layers-pass.d.ts +5 -2
  68. package/dist/passes/pick-layers-pass.d.ts.map +1 -1
  69. package/dist/passes/pick-layers-pass.js +3 -2
  70. package/dist/passes/pick-layers-pass.js.map +1 -1
  71. package/dist/shaderlib/project/project.glsl.d.ts.map +1 -1
  72. package/dist/shaderlib/project/project.glsl.js +3 -0
  73. package/dist/shaderlib/project/project.glsl.js.map +1 -1
  74. package/dist/utils/deep-merge.d.ts +5 -0
  75. package/dist/utils/deep-merge.d.ts.map +1 -0
  76. package/dist/utils/deep-merge.js +31 -0
  77. package/dist/utils/deep-merge.js.map +1 -0
  78. package/dist/utils/math-utils.d.ts +4 -0
  79. package/dist/utils/math-utils.d.ts.map +1 -1
  80. package/dist/utils/math-utils.js +8 -0
  81. package/dist/utils/math-utils.js.map +1 -1
  82. package/dist/viewports/globe-viewport.d.ts +1 -0
  83. package/dist/viewports/globe-viewport.d.ts.map +1 -1
  84. package/dist/viewports/globe-viewport.js +1 -1
  85. package/dist/viewports/globe-viewport.js.map +1 -1
  86. package/dist/viewports/orbit-viewport.d.ts.map +1 -1
  87. package/dist/viewports/orbit-viewport.js +7 -2
  88. package/dist/viewports/orbit-viewport.js.map +1 -1
  89. package/dist/viewports/orthographic-viewport.d.ts +8 -2
  90. package/dist/viewports/orthographic-viewport.d.ts.map +1 -1
  91. package/dist/viewports/orthographic-viewport.js.map +1 -1
  92. package/dist/views/orthographic-view.d.ts +38 -4
  93. package/dist/views/orthographic-view.d.ts.map +1 -1
  94. package/dist/views/orthographic-view.js.map +1 -1
  95. package/dist/views/view.d.ts.map +1 -1
  96. package/dist/views/view.js +2 -8
  97. package/dist/views/view.js.map +1 -1
  98. package/dist.min.js +220 -144
  99. package/package.json +9 -9
  100. package/src/controllers/controller.ts +23 -9
  101. package/src/controllers/first-person-controller.ts +18 -8
  102. package/src/controllers/globe-controller.ts +89 -5
  103. package/src/controllers/map-controller.ts +105 -56
  104. package/src/controllers/orbit-controller.ts +147 -13
  105. package/src/controllers/orthographic-controller.ts +417 -41
  106. package/src/controllers/view-state.ts +8 -1
  107. package/src/debug/loggers.ts +1 -5
  108. package/src/effects/lighting/lighting-effect.ts +20 -8
  109. package/src/lib/attribute/attribute-manager.ts +1 -0
  110. package/src/lib/deck-picker.ts +18 -4
  111. package/src/lib/deck-renderer.ts +17 -3
  112. package/src/lib/deck.ts +19 -3
  113. package/src/lib/layer.ts +1 -0
  114. package/src/passes/draw-layers-pass.ts +5 -0
  115. package/src/passes/layers-pass.ts +5 -1
  116. package/src/passes/pick-layers-pass.ts +8 -4
  117. package/src/shaderlib/project/project.glsl.ts +3 -0
  118. package/src/utils/deep-merge.ts +33 -0
  119. package/src/utils/math-utils.ts +12 -0
  120. package/src/viewports/globe-viewport.ts +1 -1
  121. package/src/viewports/orbit-viewport.ts +8 -2
  122. package/src/viewports/orthographic-viewport.ts +8 -2
  123. package/src/views/orthographic-view.ts +38 -4
  124. 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.3.0-alpha.1" : 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,6 +1641,9 @@ var LayersPass = class extends Pass {
1641
1641
  this._lastRenderIndex = -1;
1642
1642
  }
1643
1643
  render(options) {
1644
+ this._render(options);
1645
+ }
1646
+ _render(options) {
1644
1647
  const canvasContext = this.device.canvasContext;
1645
1648
  const framebuffer = options.target ?? canvasContext.getCurrentFramebuffer();
1646
1649
  const [width, height] = canvasContext.getDrawingBufferSize();
@@ -2097,9 +2100,7 @@ var LightingEffect = class {
2097
2100
  } : {};
2098
2101
  const lightingProps = {
2099
2102
  enabled: true,
2100
- ambientLight: this.ambientLight,
2101
- directionalLights: this.directionalLights.map((directionalLight) => directionalLight.getProjectedLight({ layer })),
2102
- pointLights: this.pointLights.map((pointLight) => pointLight.getProjectedLight({ layer }))
2103
+ lights: this._getLights(layer)
2103
2104
  };
2104
2105
  const materialProps = layer.props.material;
2105
2106
  return {
@@ -2143,6 +2144,19 @@ var LightingEffect = class {
2143
2144
  this.directionalLights.push(new DirectionalLight(DEFAULT_DIRECTIONAL_LIGHT_PROPS[0]), new DirectionalLight(DEFAULT_DIRECTIONAL_LIGHT_PROPS[1]));
2144
2145
  }
2145
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
+ }
2146
2160
  };
2147
2161
 
2148
2162
  // dist/utils/typed-array-manager.js
@@ -2231,6 +2245,14 @@ function mod(value, divisor) {
2231
2245
  function getCameraPosition(viewMatrixInverse) {
2232
2246
  return [viewMatrixInverse[12], viewMatrixInverse[13], viewMatrixInverse[14]];
2233
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
+ }
2234
2256
  function getFrustumPlanes(viewProjectionMatrix) {
2235
2257
  return {
2236
2258
  left: getFrustumPlane(viewProjectionMatrix[3] + viewProjectionMatrix[0], viewProjectionMatrix[7] + viewProjectionMatrix[4], viewProjectionMatrix[11] + viewProjectionMatrix[8], viewProjectionMatrix[15] + viewProjectionMatrix[12]),
@@ -3099,7 +3121,8 @@ var PickLayersPass = class extends LayersPass {
3099
3121
  if ("pickingFBO" in props) {
3100
3122
  return this._drawPickingBuffer(props);
3101
3123
  }
3102
- return super.render(props);
3124
+ const stats = super._render(props);
3125
+ return { decodePickingColor: null, stats };
3103
3126
  }
3104
3127
  // Private
3105
3128
  // Draws list of layers and viewports into the picking buffer
@@ -3108,7 +3131,7 @@ var PickLayersPass = class extends LayersPass {
3108
3131
  this.pickZ = pickZ;
3109
3132
  const colorEncoderState = this._resetColorEncoder(pickZ);
3110
3133
  const scissorRect = [x, y, width, height];
3111
- const renderStatus = super.render({
3134
+ const renderStatus = super._render({
3112
3135
  target: pickingFBO,
3113
3136
  layers,
3114
3137
  layerFilter,
@@ -4181,6 +4204,31 @@ function isAddSubSymbol(token) {
4181
4204
  return Boolean(token && token.type === "symbol" && (token.value === "+" || token.value === "-"));
4182
4205
  }
4183
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
+
4184
4232
  // dist/views/view.js
4185
4233
  var View = class {
4186
4234
  constructor(props) {
@@ -4234,13 +4282,7 @@ var View = class {
4234
4282
  if (!this.props.viewState.id) {
4235
4283
  return this.props.viewState;
4236
4284
  }
4237
- const newViewState = { ...viewState };
4238
- for (const key in this.props.viewState) {
4239
- if (key !== "id") {
4240
- newViewState[key] = this.props.viewState[key];
4241
- }
4242
- }
4243
- return newViewState;
4285
+ return deepMergeViewState(viewState, this.props.viewState);
4244
4286
  }
4245
4287
  return viewState;
4246
4288
  }
@@ -4855,6 +4897,7 @@ var Controller = class {
4855
4897
  this.onStateChange = opts.onStateChange || (() => {
4856
4898
  });
4857
4899
  this.makeViewport = opts.makeViewport;
4900
+ this.pickPosition = opts.pickPosition;
4858
4901
  }
4859
4902
  set events(customEvents) {
4860
4903
  this.toggleEvents(this._customEvents, false);
@@ -4959,6 +5002,7 @@ var Controller = class {
4959
5002
  if (props.dragMode) {
4960
5003
  this.dragMode = props.dragMode;
4961
5004
  }
5005
+ const oldProps = this.props;
4962
5006
  this.props = props;
4963
5007
  if (!("transitionInterpolator" in props)) {
4964
5008
  props.transitionInterpolator = this._getTransitionProps().transitionInterpolator;
@@ -4981,6 +5025,15 @@ var Controller = class {
4981
5025
  this.touchZoom = touchZoom;
4982
5026
  this.touchRotate = touchRotate;
4983
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
+ }
4984
5037
  }
4985
5038
  updateTransition() {
4986
5039
  this.transitionManager.updateTransition();
@@ -5032,15 +5085,13 @@ var Controller = class {
5032
5085
  if (this.invertPan || this.dragMode === "pan") {
5033
5086
  alternateMode = !alternateMode;
5034
5087
  }
5035
- const newControllerState = alternateMode ? this.controllerState.panStart({ pos }) : this.controllerState.rotateStart(this._getRotateStartParams(pos));
5088
+ const newControllerState = this.controllerState[alternateMode ? "panStart" : "rotateStart"]({
5089
+ pos
5090
+ });
5036
5091
  this._panMove = alternateMode;
5037
5092
  this.updateViewport(newControllerState, NO_TRANSITION_PROPS, { isDragging: true });
5038
5093
  return true;
5039
5094
  }
5040
- /** Returns parameters for rotateStart. Override to add extra params (e.g. altitude). */
5041
- _getRotateStartParams(pos) {
5042
- return { pos };
5043
- }
5044
5095
  // Default handler for the `panmove` and `panend` event.
5045
5096
  _onPan(event) {
5046
5097
  if (!this.isDragging()) {
@@ -5388,7 +5439,8 @@ var Controller = class {
5388
5439
 
5389
5440
  // dist/controllers/view-state.js
5390
5441
  var ViewState = class {
5391
- constructor(props, state) {
5442
+ constructor(props, state, makeViewport) {
5443
+ this.makeViewport = makeViewport;
5392
5444
  this._viewportProps = this.applyConstraints(props);
5393
5445
  this._state = state;
5394
5446
  }
@@ -5404,6 +5456,22 @@ var ViewState = class {
5404
5456
  var import_web_mercator7 = require("@math.gl/web-mercator");
5405
5457
  var PITCH_MOUSE_THRESHOLD = 5;
5406
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
+ }
5407
5475
  var MapState = class extends ViewState {
5408
5476
  constructor(options) {
5409
5477
  const {
@@ -5456,6 +5524,7 @@ var MapState = class extends ViewState {
5456
5524
  assert(Number.isFinite(longitude));
5457
5525
  assert(Number.isFinite(latitude));
5458
5526
  assert(Number.isFinite(zoom));
5527
+ const maxBounds = options.maxBounds || (normalize ? WEB_MERCATOR_MAX_BOUNDS : null);
5459
5528
  super({
5460
5529
  width,
5461
5530
  height,
@@ -5470,7 +5539,8 @@ var MapState = class extends ViewState {
5470
5539
  maxPitch,
5471
5540
  minPitch,
5472
5541
  normalize,
5473
- position
5542
+ position,
5543
+ maxBounds
5474
5544
  }, {
5475
5545
  startPanLngLat,
5476
5546
  startZoomLngLat,
@@ -5479,8 +5549,8 @@ var MapState = class extends ViewState {
5479
5549
  startBearing,
5480
5550
  startPitch,
5481
5551
  startZoom
5482
- });
5483
- this.makeViewport = options.makeViewport;
5552
+ }, options.makeViewport);
5553
+ this.getAltitude = options.getAltitude;
5484
5554
  }
5485
5555
  /**
5486
5556
  * Start panning
@@ -5518,12 +5588,10 @@ var MapState = class extends ViewState {
5518
5588
  /**
5519
5589
  * Start rotating
5520
5590
  * @param {[Number, Number]} pos - position on screen where the center is
5521
- * @param {Number} altitude - optional altitude for rotation pivot
5522
- * - undefined: rotate around viewport center (no pivot point)
5523
- * - 0: rotate around pointer position at ground level
5524
- * - other value: rotate around pointer position at specified altitude
5525
5591
  */
5526
- rotateStart({ pos, altitude }) {
5592
+ rotateStart({ pos }) {
5593
+ var _a;
5594
+ const altitude = (_a = this.getAltitude) == null ? void 0 : _a.call(this, pos);
5527
5595
  return this._getUpdatedState({
5528
5596
  startRotatePos: pos,
5529
5597
  startRotateLngLat: altitude !== void 0 ? this._unproject3D(pos, altitude) : void 0,
@@ -5601,9 +5669,7 @@ var MapState = class extends ViewState {
5601
5669
  if (!startZoomLngLat) {
5602
5670
  return this;
5603
5671
  }
5604
- const { maxZoom, minZoom } = this.getViewportProps();
5605
- let zoom = startZoom + Math.log2(scale);
5606
- zoom = (0, import_core15.clamp)(zoom, minZoom, maxZoom);
5672
+ const zoom = this._constrainZoom(startZoom + Math.log2(scale));
5607
5673
  const zoomedViewport = this.makeViewport({ ...this.getViewportProps(), zoom });
5608
5674
  return this._getUpdatedState({
5609
5675
  zoom,
@@ -5672,17 +5738,52 @@ var MapState = class extends ViewState {
5672
5738
  }
5673
5739
  // Apply any constraints (mathematical or defined by _viewportProps) to map state
5674
5740
  applyConstraints(props) {
5675
- const { maxZoom, minZoom, zoom } = props;
5676
- props.zoom = (0, import_core15.clamp)(zoom, minZoom, maxZoom);
5677
- const { maxPitch, minPitch, pitch } = props;
5678
- props.pitch = (0, import_core15.clamp)(pitch, minPitch, maxPitch);
5679
- const { normalize = true } = props;
5741
+ const { maxPitch, minPitch, pitch, longitude, bearing, normalize, maxBounds } = props;
5680
5742
  if (normalize) {
5681
- 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);
5682
5762
  }
5683
5763
  return props;
5684
5764
  }
5685
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
+ }
5686
5787
  _zoomFromCenter(scale) {
5687
5788
  const { width, height } = this.getViewportProps();
5688
5789
  return this.zoom({
@@ -5746,8 +5847,8 @@ var MapState = class extends ViewState {
5746
5847
  }
5747
5848
  };
5748
5849
  var MapController = class extends Controller {
5749
- constructor(opts) {
5750
- super(opts);
5850
+ constructor() {
5851
+ super(...arguments);
5751
5852
  this.ControllerState = MapState;
5752
5853
  this.transition = {
5753
5854
  transitionDuration: 300,
@@ -5760,23 +5861,29 @@ var MapController = class extends Controller {
5760
5861
  };
5761
5862
  this.dragMode = "pan";
5762
5863
  this.rotationPivot = "center";
5763
- this.pickPosition = opts.pickPosition;
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
+ };
5764
5878
  }
5765
5879
  setProps(props) {
5766
5880
  if ("rotationPivot" in props) {
5767
5881
  this.rotationPivot = props.rotationPivot || "center";
5768
5882
  }
5883
+ props.getAltitude = this._getAltitude;
5769
5884
  props.position = props.position || [0, 0, 0];
5770
- const oldProps = this.props;
5885
+ props.maxBounds = props.maxBounds || (props.normalize === false ? null : WEB_MERCATOR_MAX_BOUNDS);
5771
5886
  super.setProps(props);
5772
- const dimensionChanged = !oldProps || oldProps.height !== props.height;
5773
- if (dimensionChanged) {
5774
- this.updateViewport(new this.ControllerState({
5775
- makeViewport: this.makeViewport,
5776
- ...props,
5777
- ...this.state
5778
- }));
5779
- }
5780
5887
  }
5781
5888
  updateViewport(newControllerState, extraProps = null, interactionState = {}) {
5782
5889
  const state = newControllerState.getState();
@@ -5790,22 +5897,6 @@ var MapController = class extends Controller {
5790
5897
  }
5791
5898
  super.updateViewport(newControllerState, extraProps, interactionState);
5792
5899
  }
5793
- /** Add altitude to rotateStart params based on rotationPivot mode */
5794
- _getRotateStartParams(pos) {
5795
- let altitude;
5796
- if (this.rotationPivot === "2d") {
5797
- altitude = 0;
5798
- } else if (this.rotationPivot === "3d") {
5799
- if (this.pickPosition) {
5800
- const { x, y } = this.props;
5801
- const pickResult = this.pickPosition(x + pos[0], y + pos[1]);
5802
- if (pickResult && pickResult.coordinate && pickResult.coordinate.length >= 3) {
5803
- altitude = pickResult.coordinate[2];
5804
- }
5805
- }
5806
- }
5807
- return { pos, altitude };
5808
- }
5809
5900
  };
5810
5901
 
5811
5902
  // dist/views/map-view.js
@@ -5920,13 +6011,17 @@ var DrawLayersPass = class extends LayersPass {
5920
6011
  const { operation } = layer.props;
5921
6012
  return operation.includes("draw") || operation.includes("terrain");
5922
6013
  }
6014
+ render(options) {
6015
+ return this._render(options);
6016
+ }
5923
6017
  };
5924
6018
 
5925
6019
  // dist/lib/deck-renderer.js
5926
6020
  var TRACE_RENDER_LAYERS = "deckRenderer.renderLayers";
5927
6021
  var DeckRenderer = class {
5928
- constructor(device) {
6022
+ constructor(device, opts = {}) {
5929
6023
  this.device = device;
6024
+ this.stats = opts.stats;
5930
6025
  this.layerFilter = null;
5931
6026
  this.drawPickingColors = false;
5932
6027
  this.drawLayersPass = new DrawLayersPass(device);
@@ -5964,7 +6059,8 @@ var DeckRenderer = class {
5964
6059
  renderOpts.clearColor = [0, 0, 0, 0];
5965
6060
  renderOpts.clearCanvas = true;
5966
6061
  }
5967
- const renderStats = layerPass.render({ ...renderOpts, target: outputBuffer });
6062
+ const renderResult = layerPass.render({ ...renderOpts, target: outputBuffer });
6063
+ const renderStats = "stats" in renderResult ? renderResult.stats : renderResult;
5968
6064
  if (renderOpts.effects) {
5969
6065
  if (this.lastPostProcessEffect) {
5970
6066
  renderOpts.clearCanvas = opts.clearCanvas === void 0 ? true : opts.clearCanvas;
@@ -5973,6 +6069,7 @@ var DeckRenderer = class {
5973
6069
  }
5974
6070
  this.renderCount++;
5975
6071
  debug(TRACE_RENDER_LAYERS, this, renderStats, opts);
6072
+ this._updateStats(renderStats);
5976
6073
  }
5977
6074
  needsRedraw(opts = { clearRedrawFlags: false }) {
5978
6075
  const redraw = this._needsRedraw;
@@ -5988,6 +6085,15 @@ var DeckRenderer = class {
5988
6085
  }
5989
6086
  renderBuffers.length = 0;
5990
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
+ }
5991
6097
  _preRender(effects, opts) {
5992
6098
  this.lastPostProcessEffect = null;
5993
6099
  opts.preRenderStats = opts.preRenderStats || {};
@@ -6204,9 +6310,10 @@ function getViewportFromCoordinates(viewports, pixel) {
6204
6310
 
6205
6311
  // dist/lib/deck-picker.js
6206
6312
  var DeckPicker = class {
6207
- constructor(device) {
6313
+ constructor(device, opts = {}) {
6208
6314
  this._pickable = true;
6209
6315
  this.device = device;
6316
+ this.stats = opts.stats;
6210
6317
  this.pickLayersPass = new PickLayersPass(device);
6211
6318
  this.lastPickedInfo = {
6212
6319
  index: -1,
@@ -6690,7 +6797,8 @@ var DeckPicker = class {
6690
6797
  opts.preRenderStats[effect.id] = effect.preRender(opts);
6691
6798
  }
6692
6799
  }
6693
- const { decodePickingColor } = this.pickLayersPass.render(opts);
6800
+ const { decodePickingColor, stats } = this.pickLayersPass.render(opts);
6801
+ this._updateStats(stats);
6694
6802
  const { x, y, width, height } = deviceRect;
6695
6803
  const pickedColors = new (pickZ ? Float32Array : Uint8Array)(width * height * 4);
6696
6804
  this.device.readPixelsToArrayWebGL(pickingFBO, {
@@ -6725,7 +6833,8 @@ var DeckPicker = class {
6725
6833
  opts.preRenderStats[effect.id] = effect.preRender(opts);
6726
6834
  }
6727
6835
  }
6728
- const { decodePickingColor } = this.pickLayersPass.render(opts);
6836
+ const { decodePickingColor, stats } = this.pickLayersPass.render(opts);
6837
+ this._updateStats(stats);
6729
6838
  const { x, y, width, height } = deviceRect;
6730
6839
  const pickedColors = new (pickZ ? Float32Array : Uint8Array)(width * height * 4);
6731
6840
  this.device.readPixelsToArrayWebGL(pickingFBO, {
@@ -6737,6 +6846,15 @@ var DeckPicker = class {
6737
6846
  });
6738
6847
  return { pickedColors, decodePickingColor };
6739
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
+ }
6740
6858
  /**
6741
6859
  * Determine which layers to use for the depth (pickZ) pass.
6742
6860
  * - If a non-draped layer was picked, use just that layer.
@@ -7276,10 +7394,15 @@ var Deck = class {
7276
7394
  this.metrics = {
7277
7395
  fps: 0,
7278
7396
  setPropsTime: 0,
7397
+ layersCount: 0,
7398
+ drawLayersCount: 0,
7399
+ updateLayersCount: 0,
7400
+ updateAttributesCount: 0,
7279
7401
  updateAttributesTime: 0,
7280
7402
  framesRedrawn: 0,
7281
7403
  pickTime: 0,
7282
7404
  pickCount: 0,
7405
+ pickLayersCount: 0,
7283
7406
  gpuTime: 0,
7284
7407
  gpuTimePerFrame: 0,
7285
7408
  cpuTime: 0,
@@ -7873,8 +7996,8 @@ var Deck = class {
7873
7996
  deck: this,
7874
7997
  device: this.device
7875
7998
  });
7876
- this.deckRenderer = new DeckRenderer(this.device);
7877
- 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 });
7878
8001
  const widgetParent = ((_c = this.props.parent) == null ? void 0 : _c.querySelector(".deck-widgets-root")) || ((_d = this.canvas) == null ? void 0 : _d.parentElement);
7879
8002
  this.widgetManager = new WidgetManager({
7880
8003
  deck: this,
@@ -7957,6 +8080,7 @@ var Deck = class {
7957
8080
  stats.get("CPU Time").addTime(animationLoopStats.get("CPU Time").lastTiming);
7958
8081
  }
7959
8082
  _getMetrics() {
8083
+ var _a;
7960
8084
  const { metrics, stats } = this;
7961
8085
  metrics.fps = stats.get("frameRate").getHz();
7962
8086
  metrics.setPropsTime = stats.get("setProps Time").time;
@@ -7964,11 +8088,16 @@ var Deck = class {
7964
8088
  metrics.framesRedrawn = stats.get("Redraw Count").count;
7965
8089
  metrics.pickTime = stats.get("pickObject Time").time + stats.get("pickMultipleObjects Time").time + stats.get("pickObjects Time").time;
7966
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;
7967
8096
  metrics.gpuTime = stats.get("GPU Time").time;
7968
8097
  metrics.cpuTime = stats.get("CPU Time").time;
7969
8098
  metrics.gpuTimePerFrame = stats.get("GPU Time").getAverageTime();
7970
8099
  metrics.cpuTimePerFrame = stats.get("CPU Time").getAverageTime();
7971
- const memoryStats = import_core16.luma.stats.get("Memory Usage");
8100
+ const memoryStats = import_core16.luma.stats.get("GPU Time and Memory");
7972
8101
  metrics.bufferMemory = memoryStats.get("Buffer Memory").count;
7973
8102
  metrics.textureMemory = memoryStats.get("Texture Memory").count;
7974
8103
  metrics.renderbufferMemory = memoryStats.get("Renderbuffer Memory").count;
@@ -9651,6 +9780,8 @@ var AttributeManager = class {
9651
9780
  }
9652
9781
  if (this.stats) {
9653
9782
  this.stats.get("Update Attributes").timeEnd();
9783
+ if (updated)
9784
+ this.stats.get("Attributes updated").incrementCount();
9654
9785
  }
9655
9786
  this.attributeTransitionManager.update({
9656
9787
  attributes: this.attributes,
@@ -11436,6 +11567,7 @@ var Layer = class extends component_default {
11436
11567
  if (!stateNeedsUpdate) {
11437
11568
  return;
11438
11569
  }
11570
+ this.context.stats.get("Layer updates").incrementCount();
11439
11571
  const currentProps = this.props;
11440
11572
  const context = this.context;
11441
11573
  const internalState = this.internalState;
@@ -11932,9 +12064,13 @@ var OrbitViewport = class extends viewport_default {
11932
12064
  }
11933
12065
  panByPosition(coords, pixel, startPixel) {
11934
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;
11935
12071
  const nextCenter = [
11936
- this.width / 2 + p0[0] - pixel[0],
11937
- 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,
11938
12074
  this.projectedCenter[2]
11939
12075
  ];
11940
12076
  return {
@@ -12085,6 +12221,7 @@ var FirstPersonState = class extends ViewState {
12085
12221
  latitude = null,
12086
12222
  maxPitch = 90,
12087
12223
  minPitch = -90,
12224
+ maxBounds = null,
12088
12225
  // Model state when the rotate operation first started
12089
12226
  startRotatePos,
12090
12227
  startBearing,
@@ -12102,7 +12239,8 @@ var FirstPersonState = class extends ViewState {
12102
12239
  longitude,
12103
12240
  latitude,
12104
12241
  maxPitch,
12105
- minPitch
12242
+ minPitch,
12243
+ maxBounds
12106
12244
  }, {
12107
12245
  startRotatePos,
12108
12246
  startBearing,
@@ -12110,8 +12248,7 @@ var FirstPersonState = class extends ViewState {
12110
12248
  startZoomPosition,
12111
12249
  startPanPos,
12112
12250
  startPanPosition
12113
- });
12114
- this.makeViewport = options.makeViewport;
12251
+ }, options.makeViewport);
12115
12252
  }
12116
12253
  /* Public API */
12117
12254
  /**
@@ -12321,7 +12458,7 @@ var FirstPersonState = class extends ViewState {
12321
12458
  }
12322
12459
  // Apply any constraints (mathematical or defined by _viewportProps) to map state
12323
12460
  applyConstraints(props) {
12324
- const { pitch, maxPitch, minPitch, longitude, bearing } = props;
12461
+ const { pitch, maxPitch, minPitch, longitude, position, bearing, maxBounds } = props;
12325
12462
  props.pitch = (0, import_core26.clamp)(pitch, minPitch, maxPitch);
12326
12463
  if (longitude !== null && (longitude < -180 || longitude > 180)) {
12327
12464
  props.longitude = mod(longitude + 180, 360) - 180;
@@ -12329,6 +12466,14 @@ var FirstPersonState = class extends ViewState {
12329
12466
  if (bearing < -180 || bearing > 180) {
12330
12467
  props.bearing = mod(bearing + 180, 360) - 180;
12331
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
+ }
12332
12477
  return props;
12333
12478
  }
12334
12479
  };
@@ -12379,6 +12524,7 @@ var OrbitState = class extends ViewState {
12379
12524
  maxRotationX = 90,
12380
12525
  minZoom = -Infinity,
12381
12526
  maxZoom = Infinity,
12527
+ maxBounds = null,
12382
12528
  /** Interaction states, required to calculate change during transform */
12383
12529
  // Model state when the pan operation first started
12384
12530
  startPanPosition,
@@ -12400,7 +12546,8 @@ var OrbitState = class extends ViewState {
12400
12546
  minRotationX,
12401
12547
  maxRotationX,
12402
12548
  minZoom,
12403
- maxZoom
12549
+ maxZoom,
12550
+ maxBounds
12404
12551
  }, {
12405
12552
  startPanPosition,
12406
12553
  startRotatePos,
@@ -12408,8 +12555,8 @@ var OrbitState = class extends ViewState {
12408
12555
  startRotationOrbit,
12409
12556
  startZoomPosition,
12410
12557
  startZoom
12411
- });
12412
- this.makeViewport = options.makeViewport;
12558
+ }, options.makeViewport);
12559
+ this.unproject3D = options.unproject3D;
12413
12560
  }
12414
12561
  /**
12415
12562
  * Start panning
@@ -12594,17 +12741,20 @@ var OrbitState = class extends ViewState {
12594
12741
  return viewport.project(pos);
12595
12742
  }
12596
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;
12597
12748
  const viewport = this.makeViewport(this.getViewportProps());
12598
12749
  return viewport.unproject(pos);
12599
12750
  }
12600
12751
  // Calculates new zoom
12601
12752
  _calculateNewZoom({ scale, startZoom }) {
12602
- const { maxZoom, minZoom } = this.getViewportProps();
12603
12753
  if (startZoom === void 0) {
12604
12754
  startZoom = this.getViewportProps().zoom;
12605
12755
  }
12606
12756
  const zoom = startZoom + Math.log2(scale);
12607
- return (0, import_core27.clamp)(zoom, minZoom, maxZoom);
12757
+ return this._constrainZoom(zoom);
12608
12758
  }
12609
12759
  _panFromCenter(offset) {
12610
12760
  const { target } = this.getViewportProps();
@@ -12624,14 +12774,94 @@ var OrbitState = class extends ViewState {
12624
12774
  }
12625
12775
  // Apply any constraints (mathematical or defined by _viewportProps) to map state
12626
12776
  applyConstraints(props) {
12627
- const { maxZoom, minZoom, zoom, maxRotationX, minRotationX, rotationOrbit } = props;
12628
- 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);
12629
12779
  props.rotationX = (0, import_core27.clamp)(props.rotationX, minRotationX, maxRotationX);
12630
12780
  if (rotationOrbit < -180 || rotationOrbit > 180) {
12631
12781
  props.rotationOrbit = mod(rotationOrbit + 180, 360) - 180;
12632
12782
  }
12783
+ props.target = this._constrainTarget(props);
12633
12784
  return props;
12634
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
+ }
12635
12865
  };
12636
12866
  var OrbitController = class extends Controller {
12637
12867
  constructor() {
@@ -12646,6 +12876,20 @@ var OrbitController = class extends Controller {
12646
12876
  }
12647
12877
  })
12648
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);
12649
12893
  }
12650
12894
  };
12651
12895
 
@@ -12667,41 +12911,300 @@ var orbit_view_default = OrbitView;
12667
12911
 
12668
12912
  // dist/controllers/orthographic-controller.js
12669
12913
  var import_core28 = require("@math.gl/core");
12670
- var OrthographicState = class extends OrbitState {
12671
- constructor(props) {
12672
- super(props);
12673
- 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);
12674
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]);
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
12675
13106
  _calculateNewZoom({ scale, startZoom }) {
12676
- const { maxZoom, minZoom } = this.getViewportProps();
13107
+ const { zoomX, zoomY, zoomAxis } = this.getViewportProps();
12677
13108
  if (startZoom === void 0) {
12678
- startZoom = this.getViewportProps().zoom;
13109
+ startZoom = [zoomX, zoomY];
12679
13110
  }
12680
- let deltaZoom = Math.log2(scale);
12681
- if (Array.isArray(startZoom)) {
12682
- let [newZoomX, newZoomY] = startZoom;
12683
- switch (this.zoomAxis) {
12684
- case "X":
12685
- newZoomX = (0, import_core28.clamp)(newZoomX + deltaZoom, minZoom, maxZoom);
12686
- break;
12687
- case "Y":
12688
- newZoomY = (0, import_core28.clamp)(newZoomY + deltaZoom, minZoom, maxZoom);
12689
- break;
12690
- default:
12691
- let z = Math.min(newZoomX + deltaZoom, newZoomY + deltaZoom);
12692
- if (z < minZoom) {
12693
- deltaZoom += minZoom - z;
12694
- }
12695
- z = Math.max(newZoomX + deltaZoom, newZoomY + deltaZoom);
12696
- if (z > maxZoom) {
12697
- deltaZoom += maxZoom - z;
12698
- }
12699
- newZoomX += deltaZoom;
12700
- 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;
12701
13165
  }
12702
- return [newZoomX, newZoomY];
12703
13166
  }
12704
- 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 };
12705
13208
  }
12706
13209
  };
12707
13210
  var OrthographicController = class extends Controller {
@@ -12710,10 +13213,14 @@ var OrthographicController = class extends Controller {
12710
13213
  this.ControllerState = OrthographicState;
12711
13214
  this.transition = {
12712
13215
  transitionDuration: 300,
12713
- transitionInterpolator: new LinearInterpolator(["target", "zoom"])
13216
+ transitionInterpolator: new LinearInterpolator(["target", "zoomX", "zoomY"])
12714
13217
  };
12715
13218
  this.dragMode = "pan";
12716
13219
  }
13220
+ setProps(props) {
13221
+ Object.assign(props, normalizeZoom(props));
13222
+ super.setProps(props);
13223
+ }
12717
13224
  _onPanRotate() {
12718
13225
  return false;
12719
13226
  }
@@ -12737,9 +13244,22 @@ var orthographic_view_default = OrthographicView;
12737
13244
  // dist/controllers/globe-controller.js
12738
13245
  var import_core29 = require("@math.gl/core");
12739
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
+ }
12740
13259
  var GlobeState = class extends MapState {
12741
13260
  constructor(options) {
12742
13261
  const { startPanPos, ...mapStateOptions } = options;
13262
+ mapStateOptions.normalize = false;
12743
13263
  super(mapStateOptions);
12744
13264
  if (startPanPos !== void 0) {
12745
13265
  this._state.startPanPos = startPanPos;
@@ -12778,16 +13298,58 @@ var GlobeState = class extends MapState {
12778
13298
  return this._getUpdatedState({ zoom });
12779
13299
  }
12780
13300
  applyConstraints(props) {
12781
- const { longitude, latitude, maxZoom, minZoom, zoom } = props;
12782
- const ZOOM0 = zoomAdjust(0);
12783
- const zoomAdjustment = zoomAdjust(latitude) - ZOOM0;
12784
- 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);
12785
13303
  if (longitude < -180 || longitude > 180) {
12786
13304
  props.longitude = mod(longitude + 180, 360) - 180;
12787
13305
  }
12788
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
+ }
12789
13327
  return props;
12790
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
+ }
12791
13353
  };
12792
13354
  var GlobeController = class extends Controller {
12793
13355
  constructor() {