@deck.gl/core 9.3.0-alpha.1 → 9.3.0-alpha.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/debug.min.js +1 -1
- package/dist/controllers/controller.d.ts +5 -4
- package/dist/controllers/controller.d.ts.map +1 -1
- package/dist/controllers/controller.js +18 -7
- 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 +7 -18
- package/dist/controllers/map-controller.d.ts.map +1 -1
- package/dist/controllers/map-controller.js +94 -50
- 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/terrain-controller.d.ts +29 -0
- package/dist/controllers/terrain-controller.d.ts.map +1 -0
- package/dist/controllers/terrain-controller.js +108 -0
- package/dist/controllers/terrain-controller.js.map +1 -0
- package/dist/controllers/view-state.d.ts +2 -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 +3800 -1675
- 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 +775 -123
- package/dist/index.cjs.map +4 -4
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- 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/deck-picker.d.ts +6 -1
- package/dist/lib/deck-picker.d.ts.map +1 -1
- package/dist/lib/deck-picker.js +15 -3
- 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 +5 -0
- package/dist/lib/deck.d.ts.map +1 -1
- package/dist/lib/deck.js +13 -3
- package/dist/lib/deck.js.map +1 -1
- package/dist/lib/init.js +2 -2
- package/dist/lib/layer.d.ts.map +1 -1
- package/dist/lib/layer.js +1 -0
- package/dist/lib/layer.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 +3 -0
- package/dist/passes/layers-pass.js.map +1 -1
- package/dist/passes/pick-layers-pass.d.ts +5 -2
- package/dist/passes/pick-layers-pass.d.ts.map +1 -1
- package/dist/passes/pick-layers-pass.js +3 -2
- 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/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/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 +220 -144
- package/package.json +9 -9
- package/src/controllers/controller.ts +23 -9
- package/src/controllers/first-person-controller.ts +18 -8
- package/src/controllers/globe-controller.ts +89 -5
- package/src/controllers/map-controller.ts +105 -56
- package/src/controllers/orbit-controller.ts +147 -13
- package/src/controllers/orthographic-controller.ts +417 -41
- package/src/controllers/terrain-controller.ts +146 -0
- package/src/controllers/view-state.ts +8 -1
- package/src/debug/loggers.ts +1 -5
- package/src/effects/lighting/lighting-effect.ts +20 -8
- package/src/index.ts +1 -0
- package/src/lib/attribute/attribute-manager.ts +1 -0
- package/src/lib/deck-picker.ts +18 -4
- package/src/lib/deck-renderer.ts +17 -3
- package/src/lib/deck.ts +19 -3
- package/src/lib/layer.ts +1 -0
- package/src/passes/draw-layers-pass.ts +5 -0
- package/src/passes/layers-pass.ts +5 -1
- package/src/passes/pick-layers-pass.ts +8 -4
- 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/viewports/globe-viewport.ts +1 -1
- package/src/viewports/orbit-viewport.ts +8 -2
- package/src/viewports/orthographic-viewport.ts +8 -2
- package/src/views/orthographic-view.ts +38 -4
- package/src/views/view.ts +2 -8
|
@@ -3,66 +3,442 @@
|
|
|
3
3
|
// Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
5
|
import {clamp} from '@math.gl/core';
|
|
6
|
-
import Controller from './controller';
|
|
7
|
-
import
|
|
6
|
+
import Controller, {ControllerProps} from './controller';
|
|
7
|
+
import ViewState from './view-state';
|
|
8
|
+
|
|
9
|
+
import type Viewport from '../viewports/viewport';
|
|
8
10
|
import LinearInterpolator from '../transitions/linear-interpolator';
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
export type OrthographicStateProps = {
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
target?: number[];
|
|
16
|
+
zoom?: number | number[];
|
|
17
|
+
zoomX?: number;
|
|
18
|
+
zoomY?: number;
|
|
19
|
+
zoomAxis?: 'X' | 'Y' | 'all';
|
|
20
|
+
|
|
21
|
+
/** Viewport constraints */
|
|
22
|
+
maxZoomX?: number;
|
|
23
|
+
minZoomX?: number;
|
|
24
|
+
maxZoomY?: number;
|
|
25
|
+
minZoomY?: number;
|
|
26
|
+
|
|
27
|
+
maxBounds?: ControllerProps['maxBounds'];
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type OrthographicStateInternal = {
|
|
31
|
+
startPanPosition?: number[];
|
|
32
|
+
startZoomPosition?: number[];
|
|
33
|
+
startZoom?: number[];
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
function normalizeZoom({
|
|
37
|
+
zoom = 0,
|
|
38
|
+
zoomX,
|
|
39
|
+
zoomY
|
|
40
|
+
}: {
|
|
41
|
+
zoom?: number | number[];
|
|
42
|
+
zoomX?: number;
|
|
43
|
+
zoomY?: number;
|
|
44
|
+
}): {
|
|
45
|
+
zoomX: number;
|
|
46
|
+
zoomY: number;
|
|
47
|
+
} {
|
|
48
|
+
zoomX = zoomX ?? (Array.isArray(zoom) ? zoom[0] : zoom);
|
|
49
|
+
zoomY = zoomY ?? (Array.isArray(zoom) ? zoom[1] : zoom);
|
|
50
|
+
return {zoomX, zoomY};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export class OrthographicState extends ViewState<
|
|
54
|
+
OrthographicState,
|
|
55
|
+
OrthographicStateProps,
|
|
56
|
+
OrthographicStateInternal
|
|
57
|
+
> {
|
|
58
|
+
constructor(
|
|
59
|
+
options: OrthographicStateProps &
|
|
60
|
+
OrthographicStateInternal & {
|
|
61
|
+
maxZoom?: number;
|
|
62
|
+
minZoom?: number;
|
|
63
|
+
makeViewport: (props: Record<string, any>) => Viewport;
|
|
64
|
+
}
|
|
65
|
+
) {
|
|
66
|
+
const {
|
|
67
|
+
/* Viewport arguments */
|
|
68
|
+
width, // Width of viewport
|
|
69
|
+
height, // Height of viewport
|
|
70
|
+
target = [0, 0, 0],
|
|
71
|
+
zoom = 0,
|
|
72
|
+
zoomAxis = 'all',
|
|
73
|
+
|
|
74
|
+
/* Viewport constraints */
|
|
75
|
+
minZoom = -Infinity,
|
|
76
|
+
maxZoom = Infinity,
|
|
77
|
+
minZoomX = minZoom,
|
|
78
|
+
maxZoomX = maxZoom,
|
|
79
|
+
minZoomY = minZoom,
|
|
80
|
+
maxZoomY = maxZoom,
|
|
81
|
+
|
|
82
|
+
maxBounds = null,
|
|
83
|
+
|
|
84
|
+
/** Interaction states, required to calculate change during transform */
|
|
85
|
+
// Model state when the pan operation first started
|
|
86
|
+
startPanPosition,
|
|
87
|
+
// Model state when the zoom operation first started
|
|
88
|
+
startZoomPosition,
|
|
89
|
+
startZoom
|
|
90
|
+
} = options;
|
|
91
|
+
|
|
92
|
+
const {zoomX, zoomY} = normalizeZoom(options);
|
|
93
|
+
|
|
94
|
+
super(
|
|
95
|
+
{
|
|
96
|
+
width,
|
|
97
|
+
height,
|
|
98
|
+
target,
|
|
99
|
+
zoom,
|
|
100
|
+
zoomX,
|
|
101
|
+
zoomY,
|
|
102
|
+
zoomAxis,
|
|
103
|
+
minZoomX,
|
|
104
|
+
maxZoomX,
|
|
105
|
+
minZoomY,
|
|
106
|
+
maxZoomY,
|
|
107
|
+
maxBounds
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
startPanPosition,
|
|
111
|
+
startZoomPosition,
|
|
112
|
+
startZoom
|
|
113
|
+
},
|
|
114
|
+
options.makeViewport
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Start panning
|
|
120
|
+
* @param {[Number, Number]} pos - position on screen where the pointer grabs
|
|
121
|
+
*/
|
|
122
|
+
panStart({pos}: {pos: [number, number]}): OrthographicState {
|
|
123
|
+
return this._getUpdatedState({
|
|
124
|
+
startPanPosition: this._unproject(pos)
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Pan
|
|
130
|
+
* @param {[Number, Number]} pos - position on screen where the pointer is
|
|
131
|
+
*/
|
|
132
|
+
pan({pos, startPosition}: {pos: [number, number]; startPosition?: number[]}): OrthographicState {
|
|
133
|
+
const startPanPosition = this.getState().startPanPosition || startPosition;
|
|
134
|
+
|
|
135
|
+
if (!startPanPosition) {
|
|
136
|
+
return this;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const viewport = this.makeViewport(this.getViewportProps());
|
|
140
|
+
const newProps = viewport.panByPosition(startPanPosition, pos);
|
|
141
|
+
|
|
142
|
+
return this._getUpdatedState(newProps);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* End panning
|
|
147
|
+
* Must call if `panStart()` was called
|
|
148
|
+
*/
|
|
149
|
+
panEnd(): OrthographicState {
|
|
150
|
+
return this._getUpdatedState({
|
|
151
|
+
startPanPosition: null
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Start rotating
|
|
157
|
+
*/
|
|
158
|
+
rotateStart(): OrthographicState {
|
|
159
|
+
return this;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Rotate
|
|
164
|
+
*/
|
|
165
|
+
rotate(): OrthographicState {
|
|
166
|
+
return this;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* End rotating
|
|
171
|
+
*/
|
|
172
|
+
rotateEnd(): OrthographicState {
|
|
173
|
+
return this;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// shortest path between two view states
|
|
177
|
+
shortestPathFrom(viewState: OrthographicState): OrthographicStateProps {
|
|
178
|
+
const fromProps = viewState.getViewportProps();
|
|
179
|
+
const props = {...this.getViewportProps()};
|
|
180
|
+
return props;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Start zooming
|
|
185
|
+
* @param {[Number, Number]} pos - position on screen where the pointer grabs
|
|
186
|
+
*/
|
|
187
|
+
zoomStart({pos}: {pos: [number, number]}): OrthographicState {
|
|
188
|
+
const {zoomX, zoomY} = this.getViewportProps();
|
|
189
|
+
return this._getUpdatedState({
|
|
190
|
+
startZoomPosition: this._unproject(pos),
|
|
191
|
+
startZoom: [zoomX, zoomY]
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* Zoom
|
|
197
|
+
* @param {[Number, Number]} pos - position on screen where the current target is
|
|
198
|
+
* @param {[Number, Number]} startPos - the target position at
|
|
199
|
+
* the start of the operation. Must be supplied of `zoomStart()` was not called
|
|
200
|
+
* @param {Number} scale - a number between [0, 1] specifying the accumulated
|
|
201
|
+
* relative scale.
|
|
202
|
+
*/
|
|
203
|
+
zoom({
|
|
204
|
+
pos,
|
|
205
|
+
startPos,
|
|
206
|
+
scale
|
|
207
|
+
}: {
|
|
208
|
+
pos: [number, number];
|
|
209
|
+
startPos?: [number, number];
|
|
210
|
+
scale: number;
|
|
211
|
+
}): OrthographicState {
|
|
212
|
+
let {startZoom, startZoomPosition} = this.getState();
|
|
213
|
+
if (!startZoomPosition) {
|
|
214
|
+
// We have two modes of zoom:
|
|
215
|
+
// scroll zoom that are discrete events (transform from the current zoom level),
|
|
216
|
+
// and pinch zoom that are continuous events (transform from the zoom level when
|
|
217
|
+
// pinch started).
|
|
218
|
+
// If startZoom state is defined, then use the startZoom state;
|
|
219
|
+
// otherwise assume discrete zooming
|
|
220
|
+
const {zoomX, zoomY} = this.getViewportProps();
|
|
221
|
+
startZoom = [zoomX, zoomY];
|
|
222
|
+
startZoomPosition = this._unproject(startPos || pos);
|
|
223
|
+
}
|
|
224
|
+
if (!startZoomPosition) {
|
|
225
|
+
return this;
|
|
226
|
+
}
|
|
227
|
+
const newZoomProps = this._constrainZoom(this._calculateNewZoom({scale, startZoom}));
|
|
228
|
+
const zoomedViewport = this.makeViewport({...this.getViewportProps(), ...newZoomProps});
|
|
229
|
+
|
|
230
|
+
return this._getUpdatedState({
|
|
231
|
+
...newZoomProps,
|
|
232
|
+
...zoomedViewport.panByPosition(startZoomPosition, pos)
|
|
233
|
+
});
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* End zooming
|
|
238
|
+
* Must call if `zoomStart()` was called
|
|
239
|
+
*/
|
|
240
|
+
zoomEnd(): OrthographicState {
|
|
241
|
+
return this._getUpdatedState({
|
|
242
|
+
startZoomPosition: null,
|
|
243
|
+
startZoom: null
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
zoomIn(speed: number = 2): OrthographicState {
|
|
248
|
+
return this._getUpdatedState(this._calculateNewZoom({scale: speed}));
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
zoomOut(speed: number = 2): OrthographicState {
|
|
252
|
+
return this._getUpdatedState(this._calculateNewZoom({scale: 1 / speed}));
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
moveLeft(speed: number = 50): OrthographicState {
|
|
256
|
+
return this._panFromCenter([-speed, 0]);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
moveRight(speed: number = 50): OrthographicState {
|
|
260
|
+
return this._panFromCenter([speed, 0]);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
moveUp(speed: number = 50): OrthographicState {
|
|
264
|
+
return this._panFromCenter([0, -speed]);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
moveDown(speed: number = 50): OrthographicState {
|
|
268
|
+
return this._panFromCenter([0, speed]);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
rotateLeft(speed: number = 15): OrthographicState {
|
|
272
|
+
return this;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
rotateRight(speed: number = 15): OrthographicState {
|
|
276
|
+
return this;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
rotateUp(speed: number = 10): OrthographicState {
|
|
280
|
+
return this;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
rotateDown(speed: number = 10): OrthographicState {
|
|
284
|
+
return this;
|
|
285
|
+
}
|
|
12
286
|
|
|
13
|
-
|
|
14
|
-
super(props);
|
|
287
|
+
/* Private methods */
|
|
15
288
|
|
|
16
|
-
|
|
289
|
+
_project(pos: number[]): number[] {
|
|
290
|
+
const viewport = this.makeViewport(this.getViewportProps());
|
|
291
|
+
return viewport.project(pos);
|
|
292
|
+
}
|
|
293
|
+
_unproject(pos: number[]): number[] {
|
|
294
|
+
const viewport = this.makeViewport(this.getViewportProps());
|
|
295
|
+
return viewport.unproject(pos);
|
|
17
296
|
}
|
|
18
297
|
|
|
19
|
-
|
|
20
|
-
|
|
298
|
+
// Calculates new zoom
|
|
299
|
+
_calculateNewZoom({scale, startZoom}: {scale: number; startZoom?: number[]}): {
|
|
300
|
+
zoomX: number;
|
|
301
|
+
zoomY: number;
|
|
302
|
+
} {
|
|
303
|
+
const {zoomX, zoomY, zoomAxis} = this.getViewportProps();
|
|
21
304
|
if (startZoom === undefined) {
|
|
22
|
-
startZoom =
|
|
305
|
+
startZoom = [zoomX, zoomY];
|
|
306
|
+
}
|
|
307
|
+
const deltaZoom = Math.log2(scale);
|
|
308
|
+
let [newZoomX, newZoomY] = startZoom;
|
|
309
|
+
switch (zoomAxis) {
|
|
310
|
+
case 'X':
|
|
311
|
+
// Scale x only
|
|
312
|
+
newZoomX += deltaZoom;
|
|
313
|
+
break;
|
|
314
|
+
case 'Y':
|
|
315
|
+
// Scale y only
|
|
316
|
+
newZoomY += deltaZoom;
|
|
317
|
+
break;
|
|
318
|
+
default:
|
|
319
|
+
// Lock aspect ratio
|
|
320
|
+
newZoomX += deltaZoom;
|
|
321
|
+
newZoomY += deltaZoom;
|
|
322
|
+
}
|
|
323
|
+
return {
|
|
324
|
+
zoomX: newZoomX,
|
|
325
|
+
zoomY: newZoomY
|
|
326
|
+
};
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
_panFromCenter(offset) {
|
|
330
|
+
const {target} = this.getViewportProps();
|
|
331
|
+
const center = this._project(target);
|
|
332
|
+
return this.pan({
|
|
333
|
+
startPosition: target,
|
|
334
|
+
pos: [center[0] + offset[0], center[1] + offset[1]]
|
|
335
|
+
});
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
_getUpdatedState(newProps): OrthographicState {
|
|
339
|
+
// @ts-ignore
|
|
340
|
+
return new this.constructor({
|
|
341
|
+
makeViewport: this.makeViewport,
|
|
342
|
+
...this.getViewportProps(),
|
|
343
|
+
...this.getState(),
|
|
344
|
+
...newProps
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Apply any constraints (mathematical or defined by _viewportProps) to map state
|
|
349
|
+
applyConstraints(props: Required<OrthographicStateProps>): Required<OrthographicStateProps> {
|
|
350
|
+
// Ensure zoom is within specified range
|
|
351
|
+
const {zoomX, zoomY} = this._constrainZoom(props, props);
|
|
352
|
+
props.zoomX = zoomX;
|
|
353
|
+
props.zoomY = zoomY;
|
|
354
|
+
// Backward compatibility: update zoom to reflect new view state
|
|
355
|
+
// zoom will always be ignored when zoomX and zoomY are specified, but legacy apps may still read zoom in `onViewStateChange`
|
|
356
|
+
props.zoom =
|
|
357
|
+
Array.isArray(props.zoom) || props.zoomX !== props.zoomY
|
|
358
|
+
? [props.zoomX, props.zoomY]
|
|
359
|
+
: props.zoomX;
|
|
360
|
+
|
|
361
|
+
const {maxBounds, target} = props;
|
|
362
|
+
if (maxBounds) {
|
|
363
|
+
// only calculate center and zoom ranges at rotation=0
|
|
364
|
+
// to maintain visual stability when rotating
|
|
365
|
+
const halfWidth = props.width / 2 / 2 ** zoomX;
|
|
366
|
+
const halfHeight = props.height / 2 / 2 ** zoomY;
|
|
367
|
+
const minX = maxBounds[0][0] + halfWidth;
|
|
368
|
+
const maxX = maxBounds[1][0] - halfWidth;
|
|
369
|
+
const minY = maxBounds[0][1] + halfHeight;
|
|
370
|
+
const maxY = maxBounds[1][1] - halfHeight;
|
|
371
|
+
const x = clamp(target[0], minX, maxX);
|
|
372
|
+
const y = clamp(target[1], minY, maxY);
|
|
373
|
+
if (x !== target[0] || y !== target[1]) {
|
|
374
|
+
props.target = target.slice();
|
|
375
|
+
props.target[0] = x;
|
|
376
|
+
props.target[1] = y;
|
|
377
|
+
}
|
|
23
378
|
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
newZoomX += deltaZoom;
|
|
47
|
-
newZoomY += deltaZoom;
|
|
379
|
+
return props;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
_constrainZoom(
|
|
383
|
+
{zoomX, zoomY}: {zoomX: number; zoomY: number},
|
|
384
|
+
props?: Required<OrthographicStateProps>
|
|
385
|
+
): {zoomX: number; zoomY: number} {
|
|
386
|
+
props ||= this.getViewportProps();
|
|
387
|
+
const {zoomAxis, maxZoomX, maxZoomY, maxBounds} = props;
|
|
388
|
+
let {minZoomX, minZoomY} = props;
|
|
389
|
+
const shouldApplyMaxBounds = maxBounds !== null && props.width > 0 && props.height > 0;
|
|
390
|
+
|
|
391
|
+
if (shouldApplyMaxBounds) {
|
|
392
|
+
const bl = maxBounds[0];
|
|
393
|
+
const tr = maxBounds[1];
|
|
394
|
+
const w = tr[0] - bl[0];
|
|
395
|
+
const h = tr[1] - bl[1];
|
|
396
|
+
// ignore bound size of 0 or Infinity
|
|
397
|
+
if (Number.isFinite(w) && w > 0) {
|
|
398
|
+
minZoomX = Math.max(minZoomX, Math.log2(props.width / w));
|
|
399
|
+
if (minZoomX > maxZoomX) minZoomX = maxZoomX;
|
|
48
400
|
}
|
|
49
|
-
|
|
401
|
+
if (Number.isFinite(h) && h > 0) {
|
|
402
|
+
minZoomY = Math.max(minZoomY, Math.log2(props.height / h));
|
|
403
|
+
if (minZoomY > maxZoomY) minZoomY = maxZoomY;
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
switch (zoomAxis) {
|
|
408
|
+
case 'X':
|
|
409
|
+
zoomX = clamp(zoomX, minZoomX, maxZoomX);
|
|
410
|
+
break;
|
|
411
|
+
case 'Y':
|
|
412
|
+
zoomY = clamp(zoomY, minZoomY, maxZoomY);
|
|
413
|
+
break;
|
|
414
|
+
default:
|
|
415
|
+
// Lock aspect ratio
|
|
416
|
+
let delta = Math.min(maxZoomX - zoomX, maxZoomY - zoomY, 0);
|
|
417
|
+
if (delta === 0) {
|
|
418
|
+
delta = Math.max(minZoomX - zoomX, minZoomY - zoomY, 0);
|
|
419
|
+
}
|
|
420
|
+
if (delta !== 0) {
|
|
421
|
+
zoomX += delta;
|
|
422
|
+
zoomY += delta;
|
|
423
|
+
}
|
|
50
424
|
}
|
|
51
|
-
|
|
52
|
-
// `LinearTransitionInterpolator` does not support interpolation between a number and an array
|
|
53
|
-
// So if zoom is a number (legacy use case), new zoom still has to be a number
|
|
54
|
-
return clamp(startZoom + deltaZoom, minZoom, maxZoom);
|
|
425
|
+
return {zoomX, zoomY};
|
|
55
426
|
}
|
|
56
427
|
}
|
|
57
428
|
|
|
58
|
-
export default class OrthographicController extends Controller<
|
|
429
|
+
export default class OrthographicController extends Controller<OrthographicState> {
|
|
59
430
|
ControllerState = OrthographicState;
|
|
60
431
|
transition = {
|
|
61
432
|
transitionDuration: 300,
|
|
62
|
-
transitionInterpolator: new LinearInterpolator(['target', '
|
|
433
|
+
transitionInterpolator: new LinearInterpolator(['target', 'zoomX', 'zoomY'])
|
|
63
434
|
};
|
|
64
435
|
dragMode: 'pan' | 'rotate' = 'pan';
|
|
65
436
|
|
|
437
|
+
setProps(props: ControllerProps & OrthographicStateProps) {
|
|
438
|
+
Object.assign(props, normalizeZoom(props));
|
|
439
|
+
super.setProps(props);
|
|
440
|
+
}
|
|
441
|
+
|
|
66
442
|
_onPanRotate() {
|
|
67
443
|
// No rotation in orthographic view
|
|
68
444
|
return false;
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// deck.gl
|
|
2
|
+
// SPDX-License-Identifier: MIT
|
|
3
|
+
// Copyright (c) vis.gl contributors
|
|
4
|
+
|
|
5
|
+
import MapController from './map-controller';
|
|
6
|
+
import {MapState, MapStateProps} from './map-controller';
|
|
7
|
+
import type {ControllerProps, InteractionState} from './controller';
|
|
8
|
+
import type {MjolnirGestureEvent, MjolnirWheelEvent} from 'mjolnir.js';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Controller that extends MapController with terrain-aware behavior.
|
|
12
|
+
* The camera smoothly follows terrain elevation during pan/zoom.
|
|
13
|
+
*/
|
|
14
|
+
export default class TerrainController extends MapController {
|
|
15
|
+
/** Cached terrain altitude from depth picking at viewport center (smoothed) */
|
|
16
|
+
private _terrainAltitude?: number = undefined;
|
|
17
|
+
/** Raw (unsmoothed) terrain altitude from latest pick */
|
|
18
|
+
private _terrainAltitudeTarget?: number = undefined;
|
|
19
|
+
|
|
20
|
+
setProps(
|
|
21
|
+
props: ControllerProps &
|
|
22
|
+
MapStateProps & {
|
|
23
|
+
rotationPivot?: 'center' | '2d' | '3d';
|
|
24
|
+
getAltitude?: (pos: [number, number]) => number | undefined;
|
|
25
|
+
}
|
|
26
|
+
) {
|
|
27
|
+
super.setProps({rotationPivot: '3d', ...props});
|
|
28
|
+
|
|
29
|
+
// Drive smoothing animation when terrain altitude hasn't converged yet.
|
|
30
|
+
if (
|
|
31
|
+
this._terrainAltitude !== undefined &&
|
|
32
|
+
this._terrainAltitudeTarget !== undefined &&
|
|
33
|
+
Math.abs(this._terrainAltitudeTarget - this._terrainAltitude) > 0.01
|
|
34
|
+
) {
|
|
35
|
+
this.updateViewport(
|
|
36
|
+
new this.ControllerState({
|
|
37
|
+
makeViewport: this.makeViewport,
|
|
38
|
+
...this.props,
|
|
39
|
+
...this.state
|
|
40
|
+
} as any)
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
protected updateViewport(
|
|
46
|
+
newControllerState: MapState,
|
|
47
|
+
extraProps: Record<string, any> | null = null,
|
|
48
|
+
interactionState: InteractionState = {}
|
|
49
|
+
): void {
|
|
50
|
+
const SMOOTHING = 0.05;
|
|
51
|
+
|
|
52
|
+
// No interactions yet, do not update
|
|
53
|
+
if (this._terrainAltitudeTarget === undefined) return;
|
|
54
|
+
|
|
55
|
+
if (this._terrainAltitude === undefined) {
|
|
56
|
+
// First interaction, rebase to avoid jump
|
|
57
|
+
this._terrainAltitude = this._terrainAltitudeTarget;
|
|
58
|
+
extraProps = this._rebaseViewport(
|
|
59
|
+
this._terrainAltitudeTarget,
|
|
60
|
+
newControllerState,
|
|
61
|
+
extraProps
|
|
62
|
+
);
|
|
63
|
+
} else {
|
|
64
|
+
// Standard interaction, smoothly blend target into actual altitude
|
|
65
|
+
this._terrainAltitude += (this._terrainAltitudeTarget - this._terrainAltitude) * SMOOTHING;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const viewportProps = newControllerState.getViewportProps();
|
|
69
|
+
const pos = viewportProps.position || [0, 0, 0];
|
|
70
|
+
extraProps = {
|
|
71
|
+
...extraProps,
|
|
72
|
+
position: [pos[0], pos[1], this._terrainAltitude]
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
super.updateViewport(newControllerState, extraProps, interactionState);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
protected _onPanStart(event: MjolnirGestureEvent): boolean {
|
|
79
|
+
this._pickTerrainCenterAltitude();
|
|
80
|
+
return super._onPanStart(event);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
protected _onWheel(event: MjolnirWheelEvent): boolean {
|
|
84
|
+
this._pickTerrainCenterAltitude();
|
|
85
|
+
return super._onWheel(event);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
protected _onDoubleClick(event: MjolnirGestureEvent): boolean {
|
|
89
|
+
this._pickTerrainCenterAltitude();
|
|
90
|
+
return super._onDoubleClick(event);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
private _pickTerrainCenterAltitude(): void {
|
|
94
|
+
// TODO use async picking?
|
|
95
|
+
if (!this.pickPosition) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const {x, y, width, height} = this.props;
|
|
99
|
+
const pickResult = this.pickPosition(x + width / 2, y + height / 2);
|
|
100
|
+
if (pickResult?.coordinate && pickResult.coordinate.length >= 3) {
|
|
101
|
+
this._terrainAltitudeTarget = pickResult.coordinate[2];
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Utility function to return viewport that looks the same, but with
|
|
107
|
+
* a position shifted to [0, 0, altitude]
|
|
108
|
+
*/
|
|
109
|
+
private _rebaseViewport(
|
|
110
|
+
altitude: number,
|
|
111
|
+
newControllerState: MapState,
|
|
112
|
+
extraProps: Record<string, any> | null
|
|
113
|
+
): Record<string, any> | null {
|
|
114
|
+
const viewportProps = newControllerState.getViewportProps();
|
|
115
|
+
const oldViewport = this.makeViewport({...viewportProps, position: [0, 0, 0]});
|
|
116
|
+
const oldCameraPos = oldViewport.cameraPosition;
|
|
117
|
+
|
|
118
|
+
const centerZOffset = altitude * oldViewport.distanceScales.unitsPerMeter[2];
|
|
119
|
+
const cameraHeightAboveOldCenter = oldCameraPos[2];
|
|
120
|
+
const newCameraHeightAboveCenter = cameraHeightAboveOldCenter - centerZOffset;
|
|
121
|
+
if (newCameraHeightAboveCenter <= 0) {
|
|
122
|
+
return extraProps;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const zoomDelta = Math.log2(cameraHeightAboveOldCenter / newCameraHeightAboveCenter);
|
|
126
|
+
const newZoom = viewportProps.zoom + zoomDelta;
|
|
127
|
+
|
|
128
|
+
const newViewport = this.makeViewport({
|
|
129
|
+
...viewportProps,
|
|
130
|
+
zoom: newZoom,
|
|
131
|
+
position: [0, 0, altitude]
|
|
132
|
+
});
|
|
133
|
+
const {width, height} = viewportProps;
|
|
134
|
+
const screenCenter: [number, number] = [width / 2, height / 2];
|
|
135
|
+
const worldPoint = oldViewport.unproject(screenCenter, {targetZ: altitude});
|
|
136
|
+
if (
|
|
137
|
+
worldPoint &&
|
|
138
|
+
'panByPosition3D' in newViewport &&
|
|
139
|
+
typeof newViewport.panByPosition3D === 'function'
|
|
140
|
+
) {
|
|
141
|
+
const adjusted = newViewport.panByPosition3D(worldPoint, screenCenter);
|
|
142
|
+
return {...extraProps, position: [0, 0, altitude], zoom: newZoom, ...adjusted};
|
|
143
|
+
}
|
|
144
|
+
return extraProps;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -13,7 +13,14 @@ export default abstract class ViewState<
|
|
|
13
13
|
private _viewportProps: Required<Props>;
|
|
14
14
|
private _state: State;
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
makeViewport: (props: Record<string, any>) => Viewport;
|
|
17
|
+
|
|
18
|
+
constructor(
|
|
19
|
+
props: Required<Props>,
|
|
20
|
+
state: State,
|
|
21
|
+
makeViewport: (props: Record<string, any>) => Viewport
|
|
22
|
+
) {
|
|
23
|
+
this.makeViewport = makeViewport;
|
|
17
24
|
this._viewportProps = this.applyConstraints(props);
|
|
18
25
|
this._state = state;
|
|
19
26
|
}
|
package/src/debug/loggers.ts
CHANGED
|
@@ -123,7 +123,7 @@ export const getLoggers = (log: Log): Record<string, Function> => ({
|
|
|
123
123
|
/* Render events */
|
|
124
124
|
|
|
125
125
|
'deckRenderer.renderLayers': (deckRenderer, renderStats, opts) => {
|
|
126
|
-
const {pass, redrawReason
|
|
126
|
+
const {pass, redrawReason} = opts;
|
|
127
127
|
for (const status of renderStats) {
|
|
128
128
|
const {totalCount, visibleCount, compositeCount, pickableCount} = status;
|
|
129
129
|
const primitiveCount = totalCount - compositeCount;
|
|
@@ -135,10 +135,6 @@ export const getLoggers = (log: Log): Record<string, Function> => ({
|
|
|
135
135
|
${visibleCount} (of ${totalCount} layers) to ${pass} because ${redrawReason} \
|
|
136
136
|
(${hiddenCount} hidden, ${compositeCount} composite ${pickableCount} pickable)`
|
|
137
137
|
)();
|
|
138
|
-
|
|
139
|
-
if (stats) {
|
|
140
|
-
stats.get('Redraw Layers').add(visibleCount);
|
|
141
|
-
}
|
|
142
138
|
}
|
|
143
139
|
}
|
|
144
140
|
});
|