@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.
- package/debug.min.js +1 -1
- package/dist/controllers/controller.d.ts +10 -0
- package/dist/controllers/controller.d.ts.map +1 -1
- package/dist/controllers/controller.js +15 -0
- package/dist/controllers/controller.js.map +1 -1
- package/dist/controllers/first-person-controller.d.ts +3 -2
- package/dist/controllers/first-person-controller.d.ts.map +1 -1
- package/dist/controllers/first-person-controller.js +13 -5
- package/dist/controllers/first-person-controller.js.map +1 -1
- package/dist/controllers/globe-controller.d.ts +1 -0
- package/dist/controllers/globe-controller.d.ts.map +1 -1
- package/dist/controllers/globe-controller.js +66 -5
- package/dist/controllers/globe-controller.js.map +1 -1
- package/dist/controllers/map-controller.d.ts +21 -3
- package/dist/controllers/map-controller.d.ts.map +1 -1
- package/dist/controllers/map-controller.js +139 -25
- package/dist/controllers/map-controller.js.map +1 -1
- package/dist/controllers/orbit-controller.d.ts +12 -4
- package/dist/controllers/orbit-controller.d.ts.map +1 -1
- package/dist/controllers/orbit-controller.js +118 -10
- package/dist/controllers/orbit-controller.js.map +1 -1
- package/dist/controllers/orthographic-controller.d.ts +117 -9
- package/dist/controllers/orthographic-controller.d.ts.map +1 -1
- package/dist/controllers/orthographic-controller.js +302 -37
- package/dist/controllers/orthographic-controller.js.map +1 -1
- package/dist/controllers/view-state.d.ts +4 -1
- package/dist/controllers/view-state.d.ts.map +1 -1
- package/dist/controllers/view-state.js +2 -1
- package/dist/controllers/view-state.js.map +1 -1
- package/dist/debug/loggers.d.ts.map +1 -1
- package/dist/debug/loggers.js +1 -4
- package/dist/debug/loggers.js.map +1 -1
- package/dist/dist.dev.js +7585 -10714
- package/dist/effects/lighting/lighting-effect.d.ts +1 -0
- package/dist/effects/lighting/lighting-effect.d.ts.map +1 -1
- package/dist/effects/lighting/lighting-effect.js +14 -5
- package/dist/effects/lighting/lighting-effect.js.map +1 -1
- package/dist/index.cjs +812 -120
- package/dist/index.cjs.map +4 -4
- package/dist/lib/attribute/attribute-manager.d.ts.map +1 -1
- package/dist/lib/attribute/attribute-manager.js +2 -0
- package/dist/lib/attribute/attribute-manager.js.map +1 -1
- package/dist/lib/attribute/data-column.js +2 -2
- package/dist/lib/attribute/data-column.js.map +1 -1
- package/dist/lib/attribute/gl-utils.d.ts +1 -1
- package/dist/lib/attribute/gl-utils.d.ts.map +1 -1
- package/dist/lib/attribute/gl-utils.js +4 -0
- package/dist/lib/attribute/gl-utils.js.map +1 -1
- package/dist/lib/deck-picker.d.ts +14 -1
- package/dist/lib/deck-picker.d.ts.map +1 -1
- package/dist/lib/deck-picker.js +43 -11
- package/dist/lib/deck-picker.js.map +1 -1
- package/dist/lib/deck-renderer.d.ts +6 -1
- package/dist/lib/deck-renderer.d.ts.map +1 -1
- package/dist/lib/deck-renderer.js +14 -2
- package/dist/lib/deck-renderer.js.map +1 -1
- package/dist/lib/deck.d.ts +10 -0
- package/dist/lib/deck.d.ts.map +1 -1
- package/dist/lib/deck.js +29 -11
- package/dist/lib/deck.js.map +1 -1
- package/dist/lib/init.js +2 -2
- package/dist/lib/init.js.map +1 -1
- package/dist/lib/layer.d.ts.map +1 -1
- package/dist/lib/layer.js +7 -3
- package/dist/lib/layer.js.map +1 -1
- package/dist/lib/view-manager.d.ts +4 -0
- package/dist/lib/view-manager.d.ts.map +1 -1
- package/dist/lib/view-manager.js +6 -1
- package/dist/lib/view-manager.js.map +1 -1
- package/dist/lib/widget.d.ts +4 -0
- package/dist/lib/widget.d.ts.map +1 -1
- package/dist/lib/widget.js +11 -0
- package/dist/lib/widget.js.map +1 -1
- package/dist/passes/draw-layers-pass.d.ts +2 -0
- package/dist/passes/draw-layers-pass.d.ts.map +1 -1
- package/dist/passes/draw-layers-pass.js +3 -0
- package/dist/passes/draw-layers-pass.js.map +1 -1
- package/dist/passes/layers-pass.d.ts +2 -1
- package/dist/passes/layers-pass.d.ts.map +1 -1
- package/dist/passes/layers-pass.js +7 -3
- package/dist/passes/layers-pass.js.map +1 -1
- package/dist/passes/pick-layers-pass.d.ts +6 -3
- package/dist/passes/pick-layers-pass.d.ts.map +1 -1
- package/dist/passes/pick-layers-pass.js +12 -4
- package/dist/passes/pick-layers-pass.js.map +1 -1
- package/dist/shaderlib/project/project.glsl.d.ts.map +1 -1
- package/dist/shaderlib/project/project.glsl.js +3 -0
- package/dist/shaderlib/project/project.glsl.js.map +1 -1
- package/dist/utils/deep-merge.d.ts +5 -0
- package/dist/utils/deep-merge.d.ts.map +1 -0
- package/dist/utils/deep-merge.js +31 -0
- package/dist/utils/deep-merge.js.map +1 -0
- package/dist/utils/math-utils.d.ts +4 -0
- package/dist/utils/math-utils.d.ts.map +1 -1
- package/dist/utils/math-utils.js +8 -0
- package/dist/utils/math-utils.js.map +1 -1
- package/dist/utils/texture.d.ts.map +1 -1
- package/dist/utils/texture.js +3 -1
- package/dist/utils/texture.js.map +1 -1
- package/dist/viewports/globe-viewport.d.ts +1 -0
- package/dist/viewports/globe-viewport.d.ts.map +1 -1
- package/dist/viewports/globe-viewport.js +1 -1
- package/dist/viewports/globe-viewport.js.map +1 -1
- package/dist/viewports/orbit-viewport.d.ts.map +1 -1
- package/dist/viewports/orbit-viewport.js +7 -2
- package/dist/viewports/orbit-viewport.js.map +1 -1
- package/dist/viewports/orthographic-viewport.d.ts +8 -2
- package/dist/viewports/orthographic-viewport.d.ts.map +1 -1
- package/dist/viewports/orthographic-viewport.js.map +1 -1
- package/dist/viewports/web-mercator-viewport.d.ts +5 -0
- package/dist/viewports/web-mercator-viewport.d.ts.map +1 -1
- package/dist/viewports/web-mercator-viewport.js +9 -0
- package/dist/viewports/web-mercator-viewport.js.map +1 -1
- package/dist/views/orthographic-view.d.ts +38 -4
- package/dist/views/orthographic-view.d.ts.map +1 -1
- package/dist/views/orthographic-view.js.map +1 -1
- package/dist/views/view.d.ts.map +1 -1
- package/dist/views/view.js +2 -8
- package/dist/views/view.js.map +1 -1
- package/dist.min.js +226 -154
- package/package.json +9 -9
- package/src/controllers/controller.ts +25 -2
- package/src/controllers/first-person-controller.ts +18 -8
- package/src/controllers/globe-controller.ts +89 -5
- package/src/controllers/map-controller.ts +174 -32
- package/src/controllers/orbit-controller.ts +147 -13
- package/src/controllers/orthographic-controller.ts +417 -41
- package/src/controllers/view-state.ts +10 -3
- package/src/debug/loggers.ts +1 -5
- package/src/effects/lighting/lighting-effect.ts +20 -8
- package/src/lib/attribute/attribute-manager.ts +1 -0
- package/src/lib/attribute/data-column.ts +3 -3
- package/src/lib/attribute/gl-utils.ts +5 -1
- package/src/lib/deck-picker.ts +47 -12
- package/src/lib/deck-renderer.ts +17 -3
- package/src/lib/deck.ts +39 -11
- package/src/lib/layer.ts +7 -3
- package/src/lib/view-manager.ts +9 -1
- package/src/lib/widget.ts +14 -0
- package/src/passes/draw-layers-pass.ts +5 -0
- package/src/passes/layers-pass.ts +9 -4
- package/src/passes/pick-layers-pass.ts +18 -6
- package/src/shaderlib/project/project.glsl.ts +3 -0
- package/src/utils/deep-merge.ts +33 -0
- package/src/utils/math-utils.ts +12 -0
- package/src/utils/texture.ts +3 -1
- package/src/viewports/globe-viewport.ts +1 -1
- package/src/viewports/orbit-viewport.ts +8 -2
- package/src/viewports/orthographic-viewport.ts +8 -2
- package/src/viewports/web-mercator-viewport.ts +10 -0
- package/src/views/orthographic-view.ts +38 -4
- package/src/views/view.ts +2 -8
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"description": "deck.gl core library",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"version": "9.2
|
|
6
|
+
"version": "9.3.0-alpha.2",
|
|
7
7
|
"publishConfig": {
|
|
8
8
|
"access": "public"
|
|
9
9
|
},
|
|
@@ -40,13 +40,13 @@
|
|
|
40
40
|
"prepublishOnly": "npm run build-debugger && npm run build-bundle && npm run build-bundle -- --env=dev"
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
|
-
"@loaders.gl/core": "
|
|
44
|
-
"@loaders.gl/images": "
|
|
45
|
-
"@luma.gl/constants": "
|
|
46
|
-
"@luma.gl/core": "
|
|
47
|
-
"@luma.gl/engine": "
|
|
48
|
-
"@luma.gl/shadertools": "
|
|
49
|
-
"@luma.gl/webgl": "
|
|
43
|
+
"@loaders.gl/core": "^4.4.0-alpha.18",
|
|
44
|
+
"@loaders.gl/images": "^4.4.0-alpha.18",
|
|
45
|
+
"@luma.gl/constants": "^9.3.0-alpha.6",
|
|
46
|
+
"@luma.gl/core": "^9.3.0-alpha.6",
|
|
47
|
+
"@luma.gl/engine": "^9.3.0-alpha.6",
|
|
48
|
+
"@luma.gl/shadertools": "^9.3.0-alpha.6",
|
|
49
|
+
"@luma.gl/webgl": "^9.3.0-alpha.6",
|
|
50
50
|
"@math.gl/core": "^4.1.0",
|
|
51
51
|
"@math.gl/sun": "^4.1.0",
|
|
52
52
|
"@math.gl/types": "^4.1.0",
|
|
@@ -58,5 +58,5 @@
|
|
|
58
58
|
"gl-matrix": "^3.0.0",
|
|
59
59
|
"mjolnir.js": "^3.0.0"
|
|
60
60
|
},
|
|
61
|
-
"gitHead": "
|
|
61
|
+
"gitHead": "135d329f4a4b596ae2c16e4eb801eda30252f3bc"
|
|
62
62
|
}
|
|
@@ -7,6 +7,7 @@ import TransitionManager, {TransitionProps} from './transition-manager';
|
|
|
7
7
|
import LinearInterpolator from '../transitions/linear-interpolator';
|
|
8
8
|
import {IViewState} from './view-state';
|
|
9
9
|
import {ConstructorOf} from '../types/types';
|
|
10
|
+
import {deepEqual} from '../utils/deep-equal';
|
|
10
11
|
|
|
11
12
|
import type Viewport from '../viewports/viewport';
|
|
12
13
|
|
|
@@ -65,6 +66,8 @@ export type ControllerOptions = {
|
|
|
65
66
|
dragMode?: 'pan' | 'rotate';
|
|
66
67
|
/** Enable inertia after panning/pinching. If a number is provided, indicates the duration of time over which the velocity reduces to zero, in milliseconds. Default `false`. */
|
|
67
68
|
inertia?: boolean | number;
|
|
69
|
+
/** Bounding box of content that the controller is constrained in */
|
|
70
|
+
maxBounds?: [min: [number, number], max: [number, number]] | [min: [number, number, number], max: [number, number, number]] | null;
|
|
68
71
|
};
|
|
69
72
|
|
|
70
73
|
export type ControllerProps = {
|
|
@@ -92,6 +95,8 @@ export type InteractionState = {
|
|
|
92
95
|
isRotating?: boolean;
|
|
93
96
|
/** If the view is being zoomed, either from user input or transition */
|
|
94
97
|
isZooming?: boolean;
|
|
98
|
+
/** World coordinate [lng, lat, altitude] of rotation pivot point when rotating */
|
|
99
|
+
rotationPivotPosition?: [number, number, number];
|
|
95
100
|
}
|
|
96
101
|
|
|
97
102
|
/** Parameters passed to the onViewStateChange callback */
|
|
@@ -119,7 +124,8 @@ export default abstract class Controller<ControllerState extends IViewState<Cont
|
|
|
119
124
|
protected eventManager: EventManager;
|
|
120
125
|
protected onViewStateChange: (params: ViewStateChangeParameters) => void;
|
|
121
126
|
protected onStateChange: (state: InteractionState) => void;
|
|
122
|
-
protected makeViewport: (opts: Record<string, any>) => Viewport
|
|
127
|
+
protected makeViewport: (opts: Record<string, any>) => Viewport;
|
|
128
|
+
protected pickPosition?: (x: number, y: number) => {coordinate?: number[]} | null;
|
|
123
129
|
|
|
124
130
|
private _controllerState?: ControllerState;
|
|
125
131
|
private _events: Record<string, boolean> = {};
|
|
@@ -154,6 +160,7 @@ export default abstract class Controller<ControllerState extends IViewState<Cont
|
|
|
154
160
|
makeViewport: (opts: Record<string, any>) => Viewport;
|
|
155
161
|
onViewStateChange: (params: ViewStateChangeParameters) => void;
|
|
156
162
|
onStateChange: (state: InteractionState) => void;
|
|
163
|
+
pickPosition?: (x: number, y: number) => {coordinate?: number[]} | null;
|
|
157
164
|
}) {
|
|
158
165
|
this.transitionManager = new TransitionManager<ControllerState>({
|
|
159
166
|
...opts,
|
|
@@ -168,6 +175,7 @@ export default abstract class Controller<ControllerState extends IViewState<Cont
|
|
|
168
175
|
this.onViewStateChange = opts.onViewStateChange || (() => {});
|
|
169
176
|
this.onStateChange = opts.onStateChange || (() => {});
|
|
170
177
|
this.makeViewport = opts.makeViewport;
|
|
178
|
+
this.pickPosition = opts.pickPosition;
|
|
171
179
|
}
|
|
172
180
|
|
|
173
181
|
set events(customEvents) {
|
|
@@ -237,7 +245,7 @@ export default abstract class Controller<ControllerState extends IViewState<Cont
|
|
|
237
245
|
...this.props,
|
|
238
246
|
...this.state
|
|
239
247
|
});
|
|
240
|
-
return this._controllerState
|
|
248
|
+
return this._controllerState;
|
|
241
249
|
}
|
|
242
250
|
|
|
243
251
|
getCenter(event: MjolnirGestureEvent | MjolnirWheelEvent) : [number, number] {
|
|
@@ -288,6 +296,7 @@ export default abstract class Controller<ControllerState extends IViewState<Cont
|
|
|
288
296
|
if (props.dragMode) {
|
|
289
297
|
this.dragMode = props.dragMode;
|
|
290
298
|
}
|
|
299
|
+
const oldProps = this.props;
|
|
291
300
|
this.props = props;
|
|
292
301
|
|
|
293
302
|
if (!('transitionInterpolator' in props)) {
|
|
@@ -329,6 +338,19 @@ export default abstract class Controller<ControllerState extends IViewState<Cont
|
|
|
329
338
|
this.touchZoom = touchZoom;
|
|
330
339
|
this.touchRotate = touchRotate;
|
|
331
340
|
this.keyboard = keyboard;
|
|
341
|
+
|
|
342
|
+
// Normalize view state if maxBounds is defined
|
|
343
|
+
const dimensionChanged = !oldProps || oldProps.height !== props.height || oldProps.width !== props.width || oldProps.maxBounds !== props.maxBounds;
|
|
344
|
+
if (dimensionChanged && props.maxBounds) {
|
|
345
|
+
// Dimensions changed, try re-normalize the props
|
|
346
|
+
const controllerState = new this.ControllerState({...props, makeViewport: this.makeViewport});
|
|
347
|
+
const normalizedProps = controllerState.getViewportProps();
|
|
348
|
+
const changed = Object.keys(normalizedProps).some(key => !deepEqual(normalizedProps[key], props[key], 1));
|
|
349
|
+
if (changed) {
|
|
350
|
+
// some props are updated after normalization
|
|
351
|
+
this.updateViewport(controllerState);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
332
354
|
}
|
|
333
355
|
|
|
334
356
|
updateTransition() {
|
|
@@ -396,6 +418,7 @@ export default abstract class Controller<ControllerState extends IViewState<Cont
|
|
|
396
418
|
// invertPan is replaced by props.dragMode, keeping for backward compatibility
|
|
397
419
|
alternateMode = !alternateMode;
|
|
398
420
|
}
|
|
421
|
+
|
|
399
422
|
const newControllerState = this.controllerState[alternateMode ? 'panStart' : 'rotateStart']({
|
|
400
423
|
pos
|
|
401
424
|
});
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import Controller from './controller';
|
|
5
|
+
import Controller, {ControllerProps} from './controller';
|
|
6
6
|
import ViewState from './view-state';
|
|
7
7
|
import {mod} from '../utils/math-utils';
|
|
8
8
|
import type Viewport from '../viewports/viewport';
|
|
@@ -27,6 +27,8 @@ type FirstPersonStateProps = {
|
|
|
27
27
|
|
|
28
28
|
maxPitch?: number;
|
|
29
29
|
minPitch?: number;
|
|
30
|
+
|
|
31
|
+
maxBounds?: ControllerProps['maxBounds'];
|
|
30
32
|
};
|
|
31
33
|
|
|
32
34
|
type FirstPersonStateInternal = {
|
|
@@ -43,8 +45,6 @@ class FirstPersonState extends ViewState<
|
|
|
43
45
|
FirstPersonStateProps,
|
|
44
46
|
FirstPersonStateInternal
|
|
45
47
|
> {
|
|
46
|
-
makeViewport: (props: Record<string, any>) => Viewport;
|
|
47
|
-
|
|
48
48
|
constructor(
|
|
49
49
|
options: FirstPersonStateProps &
|
|
50
50
|
FirstPersonStateInternal & {
|
|
@@ -69,6 +69,8 @@ class FirstPersonState extends ViewState<
|
|
|
69
69
|
maxPitch = 90,
|
|
70
70
|
minPitch = -90,
|
|
71
71
|
|
|
72
|
+
maxBounds = null,
|
|
73
|
+
|
|
72
74
|
// Model state when the rotate operation first started
|
|
73
75
|
startRotatePos,
|
|
74
76
|
startBearing,
|
|
@@ -88,7 +90,8 @@ class FirstPersonState extends ViewState<
|
|
|
88
90
|
longitude,
|
|
89
91
|
latitude,
|
|
90
92
|
maxPitch,
|
|
91
|
-
minPitch
|
|
93
|
+
minPitch,
|
|
94
|
+
maxBounds
|
|
92
95
|
},
|
|
93
96
|
{
|
|
94
97
|
startRotatePos,
|
|
@@ -97,10 +100,9 @@ class FirstPersonState extends ViewState<
|
|
|
97
100
|
startZoomPosition,
|
|
98
101
|
startPanPos,
|
|
99
102
|
startPanPosition
|
|
100
|
-
}
|
|
103
|
+
},
|
|
104
|
+
options.makeViewport
|
|
101
105
|
);
|
|
102
|
-
|
|
103
|
-
this.makeViewport = options.makeViewport;
|
|
104
106
|
}
|
|
105
107
|
|
|
106
108
|
/* Public API */
|
|
@@ -366,7 +368,7 @@ class FirstPersonState extends ViewState<
|
|
|
366
368
|
// Apply any constraints (mathematical or defined by _viewportProps) to map state
|
|
367
369
|
applyConstraints(props: Required<FirstPersonStateProps>): Required<FirstPersonStateProps> {
|
|
368
370
|
// Ensure pitch and zoom are within specified range
|
|
369
|
-
const {pitch, maxPitch, minPitch, longitude, bearing} = props;
|
|
371
|
+
const {pitch, maxPitch, minPitch, longitude, position, bearing, maxBounds} = props;
|
|
370
372
|
props.pitch = clamp(pitch, minPitch, maxPitch);
|
|
371
373
|
|
|
372
374
|
// Normalize degrees
|
|
@@ -376,6 +378,14 @@ class FirstPersonState extends ViewState<
|
|
|
376
378
|
if (bearing < -180 || bearing > 180) {
|
|
377
379
|
props.bearing = mod(bearing + 180, 360) - 180;
|
|
378
380
|
}
|
|
381
|
+
if (maxBounds) {
|
|
382
|
+
const x = clamp(position[0], maxBounds[0][0], maxBounds[1][0]);
|
|
383
|
+
const y = clamp(position[1], maxBounds[0][1], maxBounds[1][1]);
|
|
384
|
+
const z = clamp(position[2] ?? 0, maxBounds[0][2] ?? 0, maxBounds[1][2] ?? 0);
|
|
385
|
+
if (x !== position[0] || y !== position[1] || z !== position[2]) {
|
|
386
|
+
props.position = [x, y, z];
|
|
387
|
+
}
|
|
388
|
+
}
|
|
379
389
|
|
|
380
390
|
return props;
|
|
381
391
|
}
|
|
@@ -9,10 +9,24 @@ import {MapState, MapStateProps} from './map-controller';
|
|
|
9
9
|
import type {MapStateInternal} from './map-controller';
|
|
10
10
|
import {mod} from '../utils/math-utils';
|
|
11
11
|
import LinearInterpolator from '../transitions/linear-interpolator';
|
|
12
|
-
import {zoomAdjust} from '../viewports/globe-viewport';
|
|
12
|
+
import {zoomAdjust, GLOBE_RADIUS} from '../viewports/globe-viewport';
|
|
13
13
|
|
|
14
14
|
import {MAX_LATITUDE} from '@math.gl/web-mercator';
|
|
15
15
|
|
|
16
|
+
const DEGREES_TO_RADIANS = Math.PI / 180;
|
|
17
|
+
const RADIANS_TO_DEGREES = 180 / Math.PI;
|
|
18
|
+
|
|
19
|
+
function degreesToPixels(angle: number, zoom: number = 0): number {
|
|
20
|
+
const radians = Math.min(180, angle) * DEGREES_TO_RADIANS;
|
|
21
|
+
const size = GLOBE_RADIUS * 2 * Math.sin(radians / 2);
|
|
22
|
+
return size * Math.pow(2, zoom);
|
|
23
|
+
}
|
|
24
|
+
function pixelsToDegrees(pixels: number, zoom: number = 0): number {
|
|
25
|
+
const size = pixels / Math.pow(2, zoom);
|
|
26
|
+
const radians = Math.asin(Math.min(1, size / GLOBE_RADIUS / 2)) * 2;
|
|
27
|
+
return radians * RADIANS_TO_DEGREES;
|
|
28
|
+
}
|
|
29
|
+
|
|
16
30
|
type GlobeStateInternal = MapStateInternal & {
|
|
17
31
|
startPanPos?: [number, number];
|
|
18
32
|
};
|
|
@@ -25,6 +39,7 @@ class GlobeState extends MapState {
|
|
|
25
39
|
}
|
|
26
40
|
) {
|
|
27
41
|
const {startPanPos, ...mapStateOptions} = options;
|
|
42
|
+
mapStateOptions.normalize = false; // disable MapState default normalization
|
|
28
43
|
super(mapStateOptions);
|
|
29
44
|
|
|
30
45
|
if (startPanPos !== undefined) {
|
|
@@ -71,19 +86,88 @@ class GlobeState extends MapState {
|
|
|
71
86
|
|
|
72
87
|
applyConstraints(props: Required<MapStateProps>): Required<MapStateProps> {
|
|
73
88
|
// Ensure zoom is within specified range
|
|
74
|
-
const {longitude, latitude,
|
|
89
|
+
const {longitude, latitude, maxBounds} = props;
|
|
75
90
|
|
|
76
|
-
|
|
77
|
-
const zoomAdjustment = zoomAdjust(latitude) - ZOOM0;
|
|
78
|
-
props.zoom = clamp(zoom, minZoom + zoomAdjustment, maxZoom + zoomAdjustment);
|
|
91
|
+
props.zoom = this._constrainZoom(props.zoom, props);
|
|
79
92
|
|
|
80
93
|
if (longitude < -180 || longitude > 180) {
|
|
81
94
|
props.longitude = mod(longitude + 180, 360) - 180;
|
|
82
95
|
}
|
|
83
96
|
props.latitude = clamp(latitude, -MAX_LATITUDE, MAX_LATITUDE);
|
|
97
|
+
if (maxBounds) {
|
|
98
|
+
props.longitude = clamp(props.longitude, maxBounds[0][0], maxBounds[1][0]);
|
|
99
|
+
props.latitude = clamp(props.latitude, maxBounds[0][1], maxBounds[1][1]);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (maxBounds) {
|
|
103
|
+
// calculate center and zoom ranges at pitch=0 and bearing=0
|
|
104
|
+
// to maintain visual stability when rotating
|
|
105
|
+
const effectiveZoom = props.zoom - zoomAdjust(latitude);
|
|
106
|
+
const lngSpan = maxBounds[1][0] - maxBounds[0][0];
|
|
107
|
+
const latSpan = maxBounds[1][1] - maxBounds[0][1];
|
|
108
|
+
if (latSpan > 0 && latSpan < MAX_LATITUDE * 2) {
|
|
109
|
+
const halfHeightDegrees =
|
|
110
|
+
Math.min(pixelsToDegrees(props.height, effectiveZoom), latSpan) / 2;
|
|
111
|
+
props.latitude = clamp(
|
|
112
|
+
props.latitude,
|
|
113
|
+
maxBounds[0][1] + halfHeightDegrees,
|
|
114
|
+
maxBounds[1][1] - halfHeightDegrees
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
if (lngSpan > 0 && lngSpan < 360) {
|
|
118
|
+
const halfWidthDegrees =
|
|
119
|
+
Math.min(
|
|
120
|
+
pixelsToDegrees(
|
|
121
|
+
props.width / Math.cos(props.latitude * DEGREES_TO_RADIANS),
|
|
122
|
+
effectiveZoom
|
|
123
|
+
),
|
|
124
|
+
lngSpan
|
|
125
|
+
) / 2;
|
|
126
|
+
props.longitude = clamp(
|
|
127
|
+
props.longitude,
|
|
128
|
+
maxBounds[0][0] + halfWidthDegrees,
|
|
129
|
+
maxBounds[1][0] - halfWidthDegrees
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (props.latitude !== latitude) {
|
|
134
|
+
props.zoom += zoomAdjust(props.latitude) - zoomAdjust(latitude);
|
|
135
|
+
}
|
|
84
136
|
|
|
85
137
|
return props;
|
|
86
138
|
}
|
|
139
|
+
|
|
140
|
+
_constrainZoom(zoom: number, props?: Required<MapStateProps>): number {
|
|
141
|
+
props ||= this.getViewportProps();
|
|
142
|
+
const {latitude, maxZoom, maxBounds} = props;
|
|
143
|
+
let {minZoom} = props;
|
|
144
|
+
const ZOOM0 = zoomAdjust(0);
|
|
145
|
+
const zoomAdjustment = zoomAdjust(latitude) - ZOOM0;
|
|
146
|
+
|
|
147
|
+
const shouldApplyMaxBounds = maxBounds !== null && props.width > 0 && props.height > 0;
|
|
148
|
+
if (shouldApplyMaxBounds) {
|
|
149
|
+
const minLatitude = maxBounds[0][1];
|
|
150
|
+
const maxLatitude = maxBounds[1][1];
|
|
151
|
+
// latitude at which the bounding box is the widest
|
|
152
|
+
const fitLatitude =
|
|
153
|
+
Math.sign(minLatitude) === Math.sign(maxLatitude)
|
|
154
|
+
? Math.min(Math.abs(minLatitude), Math.abs(maxLatitude))
|
|
155
|
+
: 0;
|
|
156
|
+
const w =
|
|
157
|
+
degreesToPixels(maxBounds[1][0] - maxBounds[0][0]) *
|
|
158
|
+
Math.cos(fitLatitude * DEGREES_TO_RADIANS);
|
|
159
|
+
const h = degreesToPixels(maxBounds[1][1] - maxBounds[0][1]);
|
|
160
|
+
if (w > 0) {
|
|
161
|
+
minZoom = Math.max(minZoom, Math.log2(props.width / w) + ZOOM0);
|
|
162
|
+
}
|
|
163
|
+
if (h > 0) {
|
|
164
|
+
minZoom = Math.max(minZoom, Math.log2(props.height / h) + ZOOM0);
|
|
165
|
+
}
|
|
166
|
+
if (minZoom > maxZoom) minZoom = maxZoom;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
return clamp(zoom, minZoom + zoomAdjustment, maxZoom + zoomAdjustment);
|
|
170
|
+
}
|
|
87
171
|
}
|
|
88
172
|
|
|
89
173
|
export default class GlobeController extends Controller<MapState> {
|
|
@@ -3,16 +3,36 @@
|
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
5
|
import {clamp} from '@math.gl/core';
|
|
6
|
-
import Controller, {ControllerProps} from './controller';
|
|
6
|
+
import Controller, {ControllerProps, InteractionState} from './controller';
|
|
7
7
|
import ViewState from './view-state';
|
|
8
|
-
import {
|
|
8
|
+
import {worldToLngLat, lngLatToWorld as _lngLatToWorld} from '@math.gl/web-mercator';
|
|
9
9
|
import assert from '../utils/assert';
|
|
10
|
+
import {mod} from '../utils/math-utils';
|
|
10
11
|
|
|
11
12
|
import LinearInterpolator from '../transitions/linear-interpolator';
|
|
12
13
|
import type Viewport from '../viewports/viewport';
|
|
13
14
|
|
|
14
15
|
const PITCH_MOUSE_THRESHOLD = 5;
|
|
15
16
|
const PITCH_ACCEL = 1.2;
|
|
17
|
+
const WEB_MERCATOR_TILE_SIZE = 512;
|
|
18
|
+
const WEB_MERCATOR_MAX_BOUNDS = [
|
|
19
|
+
[-Infinity, -90],
|
|
20
|
+
[Infinity, 90]
|
|
21
|
+
] satisfies ControllerProps['maxBounds'];
|
|
22
|
+
|
|
23
|
+
/** The web mercator utility `lngLatToWorld` throws if invalid coordinates are provided.
|
|
24
|
+
* This wrapper clamps user input to calculate common positions safely. */
|
|
25
|
+
function lngLatToWorld([lng, lat]: number[]): number[] {
|
|
26
|
+
if (Math.abs(lat) > 90) {
|
|
27
|
+
lat = Math.sign(lat) * 90;
|
|
28
|
+
}
|
|
29
|
+
if (Number.isFinite(lng)) {
|
|
30
|
+
const [x, y] = _lngLatToWorld([lng, lat]);
|
|
31
|
+
return [x, clamp(y, 0, WEB_MERCATOR_TILE_SIZE)];
|
|
32
|
+
}
|
|
33
|
+
const [, y] = _lngLatToWorld([0, lat]);
|
|
34
|
+
return [lng, clamp(y, 0, WEB_MERCATOR_TILE_SIZE)];
|
|
35
|
+
}
|
|
16
36
|
|
|
17
37
|
export type MapStateProps = {
|
|
18
38
|
/** Mapbox viewport properties */
|
|
@@ -47,6 +67,8 @@ export type MapStateProps = {
|
|
|
47
67
|
|
|
48
68
|
/** Normalize viewport props to fit map height into viewport. Default `true` */
|
|
49
69
|
normalize?: boolean;
|
|
70
|
+
|
|
71
|
+
maxBounds?: ControllerProps['maxBounds'];
|
|
50
72
|
};
|
|
51
73
|
|
|
52
74
|
export type MapStateInternal = {
|
|
@@ -57,6 +79,8 @@ export type MapStateInternal = {
|
|
|
57
79
|
startZoomLngLat?: [number, number];
|
|
58
80
|
/* Pointer position when rotation started */
|
|
59
81
|
startRotatePos?: [number, number];
|
|
82
|
+
/* The lng/lat/altitude point at the rotation pivot (where rotation started) */
|
|
83
|
+
startRotateLngLat?: [number, number, number];
|
|
60
84
|
/** Bearing when current perspective rotate operation started */
|
|
61
85
|
startBearing?: number;
|
|
62
86
|
/** Pitch when current perspective rotate operation started */
|
|
@@ -68,12 +92,18 @@ export type MapStateInternal = {
|
|
|
68
92
|
/* Utils */
|
|
69
93
|
|
|
70
94
|
export class MapState extends ViewState<MapState, MapStateProps, MapStateInternal> {
|
|
71
|
-
|
|
95
|
+
/* get optional altitude for rotation pivot
|
|
96
|
+
* - undefined: rotate around viewport center (no pivot point)
|
|
97
|
+
* - 0: rotate around pointer position at ground level
|
|
98
|
+
* - other value: rotate around pointer position at specified altitude
|
|
99
|
+
*/
|
|
100
|
+
getAltitude?: (pos: [number, number]) => number | undefined;
|
|
72
101
|
|
|
73
102
|
constructor(
|
|
74
103
|
options: MapStateProps &
|
|
75
104
|
MapStateInternal & {
|
|
76
105
|
makeViewport: (props: Record<string, any>) => Viewport;
|
|
106
|
+
getAltitude?: (pos: [number, number]) => number | undefined;
|
|
77
107
|
}
|
|
78
108
|
) {
|
|
79
109
|
const {
|
|
@@ -114,6 +144,8 @@ export class MapState extends ViewState<MapState, MapStateProps, MapStateInterna
|
|
|
114
144
|
startZoomLngLat,
|
|
115
145
|
/* Pointer position when rotation started */
|
|
116
146
|
startRotatePos,
|
|
147
|
+
/* The lng/lat point at the rotation pivot (where rotation started) */
|
|
148
|
+
startRotateLngLat,
|
|
117
149
|
/** Bearing when current perspective rotate operation started */
|
|
118
150
|
startBearing,
|
|
119
151
|
/** Pitch when current perspective rotate operation started */
|
|
@@ -129,6 +161,8 @@ export class MapState extends ViewState<MapState, MapStateProps, MapStateInterna
|
|
|
129
161
|
assert(Number.isFinite(latitude)); // `latitude` must be supplied
|
|
130
162
|
assert(Number.isFinite(zoom)); // `zoom` must be supplied
|
|
131
163
|
|
|
164
|
+
const maxBounds = options.maxBounds || (normalize ? WEB_MERCATOR_MAX_BOUNDS : null);
|
|
165
|
+
|
|
132
166
|
super(
|
|
133
167
|
{
|
|
134
168
|
width,
|
|
@@ -144,19 +178,22 @@ export class MapState extends ViewState<MapState, MapStateProps, MapStateInterna
|
|
|
144
178
|
maxPitch,
|
|
145
179
|
minPitch,
|
|
146
180
|
normalize,
|
|
147
|
-
position
|
|
181
|
+
position,
|
|
182
|
+
maxBounds
|
|
148
183
|
},
|
|
149
184
|
{
|
|
150
185
|
startPanLngLat,
|
|
151
186
|
startZoomLngLat,
|
|
152
187
|
startRotatePos,
|
|
188
|
+
startRotateLngLat,
|
|
153
189
|
startBearing,
|
|
154
190
|
startPitch,
|
|
155
191
|
startZoom
|
|
156
|
-
}
|
|
192
|
+
},
|
|
193
|
+
options.makeViewport
|
|
157
194
|
);
|
|
158
195
|
|
|
159
|
-
this.
|
|
196
|
+
this.getAltitude = options.getAltitude;
|
|
160
197
|
}
|
|
161
198
|
|
|
162
199
|
/**
|
|
@@ -203,8 +240,11 @@ export class MapState extends ViewState<MapState, MapStateProps, MapStateInterna
|
|
|
203
240
|
* @param {[Number, Number]} pos - position on screen where the center is
|
|
204
241
|
*/
|
|
205
242
|
rotateStart({pos}: {pos: [number, number]}): MapState {
|
|
243
|
+
const altitude = this.getAltitude?.(pos);
|
|
244
|
+
|
|
206
245
|
return this._getUpdatedState({
|
|
207
246
|
startRotatePos: pos,
|
|
247
|
+
startRotateLngLat: altitude !== undefined ? this._unproject3D(pos, altitude) : undefined,
|
|
208
248
|
startBearing: this.getViewportProps().bearing,
|
|
209
249
|
startPitch: this.getViewportProps().pitch
|
|
210
250
|
});
|
|
@@ -223,7 +263,7 @@ export class MapState extends ViewState<MapState, MapStateProps, MapStateInterna
|
|
|
223
263
|
deltaAngleX?: number;
|
|
224
264
|
deltaAngleY?: number;
|
|
225
265
|
}): MapState {
|
|
226
|
-
const {startRotatePos, startBearing, startPitch} = this.getState();
|
|
266
|
+
const {startRotatePos, startRotateLngLat, startBearing, startPitch} = this.getState();
|
|
227
267
|
|
|
228
268
|
if (!startRotatePos || startBearing === undefined || startPitch === undefined) {
|
|
229
269
|
return this;
|
|
@@ -237,6 +277,21 @@ export class MapState extends ViewState<MapState, MapStateProps, MapStateInterna
|
|
|
237
277
|
pitch: startPitch + deltaAngleY
|
|
238
278
|
};
|
|
239
279
|
}
|
|
280
|
+
|
|
281
|
+
// If we have a pivot point, adjust the camera position to keep the pivot point fixed
|
|
282
|
+
if (startRotateLngLat) {
|
|
283
|
+
const rotatedViewport = this.makeViewport({
|
|
284
|
+
...this.getViewportProps(),
|
|
285
|
+
...newRotation
|
|
286
|
+
});
|
|
287
|
+
// Use panByPosition3D if available (WebMercatorViewport), otherwise fall back to panByPosition
|
|
288
|
+
const panMethod = 'panByPosition3D' in rotatedViewport ? 'panByPosition3D' : 'panByPosition';
|
|
289
|
+
return this._getUpdatedState({
|
|
290
|
+
...newRotation,
|
|
291
|
+
...rotatedViewport[panMethod](startRotateLngLat, startRotatePos)
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
240
295
|
return this._getUpdatedState(newRotation);
|
|
241
296
|
}
|
|
242
297
|
|
|
@@ -246,6 +301,8 @@ export class MapState extends ViewState<MapState, MapStateProps, MapStateInterna
|
|
|
246
301
|
*/
|
|
247
302
|
rotateEnd(): MapState {
|
|
248
303
|
return this._getUpdatedState({
|
|
304
|
+
startRotatePos: null,
|
|
305
|
+
startRotateLngLat: null,
|
|
249
306
|
startBearing: null,
|
|
250
307
|
startPitch: null
|
|
251
308
|
});
|
|
@@ -296,10 +353,7 @@ export class MapState extends ViewState<MapState, MapStateProps, MapStateInterna
|
|
|
296
353
|
return this;
|
|
297
354
|
}
|
|
298
355
|
|
|
299
|
-
const
|
|
300
|
-
let zoom = (startZoom as number) + Math.log2(scale);
|
|
301
|
-
zoom = clamp(zoom, minZoom, maxZoom);
|
|
302
|
-
|
|
356
|
+
const zoom = this._constrainZoom((startZoom as number) + Math.log2(scale));
|
|
303
357
|
const zoomedViewport = this.makeViewport({...this.getViewportProps(), zoom});
|
|
304
358
|
|
|
305
359
|
return this._getUpdatedState({
|
|
@@ -384,18 +438,33 @@ export class MapState extends ViewState<MapState, MapStateProps, MapStateInterna
|
|
|
384
438
|
|
|
385
439
|
// Apply any constraints (mathematical or defined by _viewportProps) to map state
|
|
386
440
|
applyConstraints(props: Required<MapStateProps>): Required<MapStateProps> {
|
|
387
|
-
// Ensure zoom is within specified range
|
|
388
|
-
const {maxZoom, minZoom, zoom} = props;
|
|
389
|
-
props.zoom = clamp(zoom, minZoom, maxZoom);
|
|
390
|
-
|
|
391
441
|
// Ensure pitch is within specified range
|
|
392
|
-
const {maxPitch, minPitch, pitch} = props;
|
|
393
|
-
props.pitch = clamp(pitch, minPitch, maxPitch);
|
|
442
|
+
const {maxPitch, minPitch, pitch, longitude, bearing, normalize, maxBounds} = props;
|
|
394
443
|
|
|
395
|
-
// Normalize viewport props to fit map height into viewport
|
|
396
|
-
const {normalize = true} = props;
|
|
397
444
|
if (normalize) {
|
|
398
|
-
|
|
445
|
+
if (longitude < -180 || longitude > 180) {
|
|
446
|
+
props.longitude = mod(longitude + 180, 360) - 180;
|
|
447
|
+
}
|
|
448
|
+
if (bearing < -180 || bearing > 180) {
|
|
449
|
+
props.bearing = mod(bearing + 180, 360) - 180;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
props.pitch = clamp(pitch, minPitch, maxPitch);
|
|
453
|
+
|
|
454
|
+
props.zoom = this._constrainZoom(props.zoom, props);
|
|
455
|
+
|
|
456
|
+
if (maxBounds) {
|
|
457
|
+
const bl = lngLatToWorld(maxBounds[0]);
|
|
458
|
+
const tr = lngLatToWorld(maxBounds[1]);
|
|
459
|
+
// calculate center and zoom ranges at pitch=0 and bearing=0
|
|
460
|
+
// to maintain visual stability when rotating
|
|
461
|
+
const scale = 2 ** props.zoom;
|
|
462
|
+
const halfWidth = props.width / 2 / scale;
|
|
463
|
+
const halfHeight = props.height / 2 / scale;
|
|
464
|
+
const [minLng, minLat] = worldToLngLat([bl[0] + halfWidth, bl[1] + halfHeight]);
|
|
465
|
+
const [maxLng, maxLat] = worldToLngLat([tr[0] - halfWidth, tr[1] - halfHeight]);
|
|
466
|
+
props.longitude = clamp(props.longitude, minLng, maxLng);
|
|
467
|
+
props.latitude = clamp(props.latitude, minLat, maxLat);
|
|
399
468
|
}
|
|
400
469
|
|
|
401
470
|
return props;
|
|
@@ -403,6 +472,30 @@ export class MapState extends ViewState<MapState, MapStateProps, MapStateInterna
|
|
|
403
472
|
|
|
404
473
|
/* Private methods */
|
|
405
474
|
|
|
475
|
+
_constrainZoom(zoom: number, props?: Required<MapStateProps>): number {
|
|
476
|
+
props ||= this.getViewportProps();
|
|
477
|
+
const {maxZoom, maxBounds} = props;
|
|
478
|
+
|
|
479
|
+
const shouldApplyMaxBounds = maxBounds !== null && props.width > 0 && props.height > 0;
|
|
480
|
+
let {minZoom} = props;
|
|
481
|
+
|
|
482
|
+
if (shouldApplyMaxBounds) {
|
|
483
|
+
const bl = lngLatToWorld(maxBounds[0]);
|
|
484
|
+
const tr = lngLatToWorld(maxBounds[1]);
|
|
485
|
+
const w = tr[0] - bl[0];
|
|
486
|
+
const h = tr[1] - bl[1];
|
|
487
|
+
// ignore bound size of 0 or Infinity
|
|
488
|
+
if (Number.isFinite(w) && w > 0) {
|
|
489
|
+
minZoom = Math.max(minZoom, Math.log2(props.width / w));
|
|
490
|
+
}
|
|
491
|
+
if (Number.isFinite(h) && h > 0) {
|
|
492
|
+
minZoom = Math.max(minZoom, Math.log2(props.height / h));
|
|
493
|
+
}
|
|
494
|
+
if (minZoom > maxZoom) minZoom = maxZoom;
|
|
495
|
+
}
|
|
496
|
+
return clamp(zoom, minZoom, maxZoom);
|
|
497
|
+
}
|
|
498
|
+
|
|
406
499
|
_zoomFromCenter(scale) {
|
|
407
500
|
const {width, height} = this.getViewportProps();
|
|
408
501
|
return this.zoom({
|
|
@@ -435,6 +528,11 @@ export class MapState extends ViewState<MapState, MapStateProps, MapStateInterna
|
|
|
435
528
|
return pos && viewport.unproject(pos);
|
|
436
529
|
}
|
|
437
530
|
|
|
531
|
+
_unproject3D(pos: [number, number], altitude: number): [number, number, number] {
|
|
532
|
+
const viewport = this.makeViewport(this.getViewportProps());
|
|
533
|
+
return viewport.unproject(pos, {targetZ: altitude}) as [number, number, number];
|
|
534
|
+
}
|
|
535
|
+
|
|
438
536
|
_getNewRotation(
|
|
439
537
|
pos: [number, number],
|
|
440
538
|
startPos: [number, number],
|
|
@@ -502,22 +600,66 @@ export default class MapController extends Controller<MapState> {
|
|
|
502
600
|
|
|
503
601
|
dragMode: 'pan' | 'rotate' = 'pan';
|
|
504
602
|
|
|
505
|
-
|
|
603
|
+
/**
|
|
604
|
+
* Rotation pivot behavior:
|
|
605
|
+
* - 'center': Rotate around viewport center (default)
|
|
606
|
+
* - '2d': Rotate around pointer position at ground level (z=0)
|
|
607
|
+
* - '3d': Rotate around 3D picked point (requires pickPosition callback)
|
|
608
|
+
*/
|
|
609
|
+
protected rotationPivot: 'center' | '2d' | '3d' = 'center';
|
|
610
|
+
|
|
611
|
+
setProps(
|
|
612
|
+
props: ControllerProps &
|
|
613
|
+
MapStateProps & {
|
|
614
|
+
rotationPivot?: 'center' | '2d' | '3d';
|
|
615
|
+
getAltitude?: (pos: [number, number]) => number | undefined;
|
|
616
|
+
}
|
|
617
|
+
) {
|
|
618
|
+
if ('rotationPivot' in props) {
|
|
619
|
+
this.rotationPivot = props.rotationPivot || 'center';
|
|
620
|
+
}
|
|
621
|
+
// this will be passed to MapState constructor
|
|
622
|
+
props.getAltitude = this._getAltitude;
|
|
506
623
|
props.position = props.position || [0, 0, 0];
|
|
507
|
-
|
|
624
|
+
props.maxBounds =
|
|
625
|
+
props.maxBounds || (props.normalize === false ? null : WEB_MERCATOR_MAX_BOUNDS);
|
|
508
626
|
|
|
509
627
|
super.setProps(props);
|
|
628
|
+
}
|
|
510
629
|
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
630
|
+
protected updateViewport(
|
|
631
|
+
newControllerState: MapState,
|
|
632
|
+
extraProps: Record<string, any> | null = null,
|
|
633
|
+
interactionState: InteractionState = {}
|
|
634
|
+
): void {
|
|
635
|
+
// Inject rotation pivot position during rotation for visual feedback
|
|
636
|
+
const state = newControllerState.getState();
|
|
637
|
+
if (interactionState.isDragging && state.startRotateLngLat) {
|
|
638
|
+
interactionState = {
|
|
639
|
+
...interactionState,
|
|
640
|
+
rotationPivotPosition: state.startRotateLngLat
|
|
641
|
+
};
|
|
642
|
+
} else if (interactionState.isDragging === false) {
|
|
643
|
+
// Clear pivot when drag ends
|
|
644
|
+
interactionState = {...interactionState, rotationPivotPosition: undefined};
|
|
521
645
|
}
|
|
646
|
+
|
|
647
|
+
super.updateViewport(newControllerState, extraProps, interactionState);
|
|
522
648
|
}
|
|
649
|
+
|
|
650
|
+
/** Add altitude to rotateStart params based on rotationPivot mode */
|
|
651
|
+
protected _getAltitude = (pos: [number, number]): number | undefined => {
|
|
652
|
+
if (this.rotationPivot === '2d') {
|
|
653
|
+
return 0;
|
|
654
|
+
} else if (this.rotationPivot === '3d') {
|
|
655
|
+
if (this.pickPosition) {
|
|
656
|
+
const {x, y} = this.props;
|
|
657
|
+
const pickResult = this.pickPosition(x + pos[0], y + pos[1]);
|
|
658
|
+
if (pickResult && pickResult.coordinate && pickResult.coordinate.length >= 3) {
|
|
659
|
+
return pickResult.coordinate[2];
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
return undefined;
|
|
664
|
+
};
|
|
523
665
|
}
|