Skip to content

Commit

Permalink
Refactor linear transition in controllers (#5824)
Browse files Browse the repository at this point in the history
  • Loading branch information
Pessimistress authored May 27, 2021
1 parent ecd3de7 commit 743e230
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 120 deletions.
43 changes: 39 additions & 4 deletions modules/core/src/controllers/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,19 @@
// THE SOFTWARE.

/* eslint-disable max-statements, complexity */
import TransitionManager from './transition-manager';
import TransitionManager, {TRANSITION_EVENTS} from './transition-manager';
import LinearInterpolator from '../transitions/linear-interpolator';

const NO_TRANSITION_PROPS = {
transitionDuration: 0
};

const LINEAR_TRANSITION_PROPS = {
transitionDuration: 300,
transitionEasing: t => t,
transitionInterruption: TRANSITION_EVENTS.BREAK
};

const DEFAULT_INERTIA = 300;
const INERTIA_EASING = t => 1 - (1 - t) * (1 - t);

Expand All @@ -48,6 +55,15 @@ export default class Controller {
onViewStateChange: this._onTransition.bind(this),
onStateChange: this._setInteractionState.bind(this)
});

const linearTransitionProps = this.linearTransitionProps;
this._transition = linearTransitionProps && {
...LINEAR_TRANSITION_PROPS,
transitionInterpolator: new LinearInterpolator({
transitionProps: linearTransitionProps
})
};

this._events = null;
this._interactionState = {
isDragging: false
Expand All @@ -61,6 +77,10 @@ export default class Controller {
this.setProps(options);
}

get linearTransitionProps() {
return null;
}

set events(customEvents) {
this.toggleEvents(this._customEvents, false);
this.toggleEvents(customEvents, true);
Expand Down Expand Up @@ -688,8 +708,23 @@ export default class Controller {
return true;
}

_getTransitionProps() {
// Transitions on double-tap and key-down are only supported by MapController
return NO_TRANSITION_PROPS;
_getTransitionProps(opts) {
const {_transition} = this;

if (!_transition) {
return NO_TRANSITION_PROPS;
}

// Enables Transitions on double-tap and key-down events.
return opts
? {
..._transition,
transitionInterpolator: new LinearInterpolator({
...opts,
transitionProps: this.linearTransitionProps,
makeViewport: this.controllerState.makeViewport
})
}
: _transition;
}
}
14 changes: 2 additions & 12 deletions modules/core/src/controllers/first-person-controller.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import Controller from './controller';
import ViewState from './view-state';
import {mod} from '../utils/math-utils';
import LinearInterpolator from '../transitions/linear-interpolator';
import {TRANSITION_EVENTS} from './transition-manager';

import {Vector3, _SphericalCoordinates as SphericalCoordinates, clamp} from 'math.gl';

Expand All @@ -15,13 +13,6 @@ const DEFAULT_STATE = {
minPitch: -90
};

const LINEAR_TRANSITION_PROPS = {
transitionDuration: 300,
transitionEasing: t => t,
transitionInterpolator: new LinearInterpolator(['position', 'pitch', 'bearing']),
transitionInterruption: TRANSITION_EVENTS.BREAK
};

class FirstPersonState extends ViewState {
constructor({
/* Viewport arguments */
Expand Down Expand Up @@ -303,8 +294,7 @@ export default class FirstPersonController extends Controller {
super(FirstPersonState, props);
}

_getTransitionProps() {
// Enables Transitions on double-tap and key-down events.
return LINEAR_TRANSITION_PROPS;
get linearTransitionProps() {
return ['position', 'pitch', 'bearing'];
}
}
14 changes: 2 additions & 12 deletions modules/core/src/controllers/globe-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,6 @@ import Controller from './controller';

import {MapState} from './map-controller';
import {mod} from '../utils/math-utils';
import LinearInterpolator from '../transitions/linear-interpolator';
import {TRANSITION_EVENTS} from './transition-manager';

const LINEAR_TRANSITION_PROPS = {
transitionDuration: 300,
transitionEasing: t => t,
transitionInterpolator: new LinearInterpolator(['longitude', 'latitude', 'zoom']),
transitionInterruption: TRANSITION_EVENTS.BREAK
};

class GlobeState extends MapState {
// Apply any constraints (mathematical or defined by _viewportProps) to map state
Expand Down Expand Up @@ -44,8 +35,7 @@ export default class GlobeController extends Controller {
this.touchRotate = false;
}

_getTransitionProps() {
// Enables Transitions on double-tap and key-down events.
return LINEAR_TRANSITION_PROPS;
get linearTransitionProps() {
return ['longitude', 'latitude', 'zoom'];
}
}
43 changes: 6 additions & 37 deletions modules/core/src/controllers/map-controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,10 @@ import Controller from './controller';
import ViewState from './view-state';
import {normalizeViewportProps} from '@math.gl/web-mercator';
import assert from '../utils/assert';
import LinearInterpolator from '../transitions/linear-interpolator';
import {TRANSITION_EVENTS} from './transition-manager';

const PITCH_MOUSE_THRESHOLD = 5;
const PITCH_ACCEL = 1.2;

const LINEAR_TRANSITION_PROPS = {
transitionDuration: 300,
transitionEasing: t => t,
transitionInterpolator: new LinearInterpolator(),
transitionInterruption: TRANSITION_EVENTS.BREAK
};

const DEFAULT_STATE = {
pitch: 0,
bearing: 0,
Expand Down Expand Up @@ -132,12 +123,10 @@ export class MapState extends ViewState {
return this;
}

const [longitude, latitude] = this._calculateNewLngLat({startPanLngLat, pos});
const viewport = this.makeViewport(this._viewportProps);
const newProps = viewport.panByPosition(startPanLngLat, pos);

return this._getUpdatedState({
longitude,
latitude
});
return this._getUpdatedState(newProps);
}

/**
Expand Down Expand Up @@ -236,15 +225,10 @@ export class MapState extends ViewState {
const zoom = this._calculateNewZoom({scale, startZoom});

const zoomedViewport = this.makeViewport({...this._viewportProps, zoom});
const [longitude, latitude] = zoomedViewport.getMapCenterByLngLatPosition({
lngLat: startZoomLngLat,
pos
});

return this._getUpdatedState({
zoom,
longitude,
latitude
...zoomedViewport.panByPosition(startZoomLngLat, pos)
});
}

Expand Down Expand Up @@ -374,12 +358,6 @@ export class MapState extends ViewState {
return pos && viewport.unproject(pos);
}

// Calculate a new lnglat based on pixel dragging position
_calculateNewLngLat({startPanLngLat, pos}) {
const viewport = this.makeViewport(this._viewportProps);
return viewport.getMapCenterByLngLatPosition({lngLat: startPanLngLat, pos});
}

// Calculates new zoom
_calculateNewZoom({scale, startZoom}) {
const {maxZoom, minZoom} = this._viewportProps;
Expand Down Expand Up @@ -461,16 +439,7 @@ export default class MapController extends Controller {
}
}

_getTransitionProps(opts) {
// Enables Transitions on double-tap and key-down events.
return opts
? {
...LINEAR_TRANSITION_PROPS,
transitionInterpolator: new LinearInterpolator({
...opts,
makeViewport: this.controllerState.makeViewport
})
}
: LINEAR_TRANSITION_PROPS;
get linearTransitionProps() {
return ['longitude', 'latitude', 'zoom', 'bearing', 'pitch'];
}
}
14 changes: 2 additions & 12 deletions modules/core/src/controllers/orbit-controller.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import {clamp, Vector2} from 'math.gl';
import Controller from './controller';
import ViewState from './view-state';
import LinearInterpolator from '../transitions/linear-interpolator';
import {TRANSITION_EVENTS} from './transition-manager';
import {mod} from '../utils/math-utils';

const DEFAULT_STATE = {
Expand All @@ -17,13 +15,6 @@ const DEFAULT_STATE = {
maxZoom: Infinity
};

const LINEAR_TRANSITION_PROPS = {
transitionDuration: 300,
transitionEasing: t => t,
transitionInterpolator: new LinearInterpolator(['target', 'zoom', 'rotationX', 'rotationOrbit']),
transitionInterruption: TRANSITION_EVENTS.BREAK
};

/* Helpers */

const zoom2Scale = zoom => Math.pow(2, zoom);
Expand Down Expand Up @@ -373,8 +364,7 @@ export default class OrbitController extends Controller {
super(OrbitState, props);
}

_getTransitionProps() {
// Enables Transitions on double-tap and key-down events.
return LINEAR_TRANSITION_PROPS;
get linearTransitionProps() {
return ['target', 'zoom', 'rotationX', 'rotationOrbit'];
}
}
14 changes: 2 additions & 12 deletions modules/core/src/controllers/orthographic-controller.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,5 @@
import Controller from './controller';
import {OrbitState} from './orbit-controller';
import LinearInterpolator from '../transitions/linear-interpolator';
import {TRANSITION_EVENTS} from './transition-manager';

const LINEAR_TRANSITION_PROPS = {
transitionDuration: 300,
transitionEasing: t => t,
transitionInterpolator: new LinearInterpolator(['target', 'zoom']),
transitionInterruption: TRANSITION_EVENTS.BREAK
};

export default class OrthographicController extends Controller {
constructor(props) {
Expand All @@ -21,8 +12,7 @@ export default class OrthographicController extends Controller {
return false;
}

_getTransitionProps() {
// Enables Transitions on double-tap and key-down events.
return LINEAR_TRANSITION_PROPS;
get linearTransitionProps() {
return ['target', 'zoom'];
}
}
23 changes: 12 additions & 11 deletions modules/core/src/transitions/linear-interpolator.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ export default class LinearInterpolator extends TransitionInterpolator {
if (makeViewport && around) {
const startViewport = makeViewport(startProps);
const endViewport = makeViewport(endProps);
const aroundLngLat = startViewport.unproject(around);
const aroundPosition = startViewport.unproject(around);
result.start.around = around;
Object.assign(result.end, {
around: endViewport.project(aroundLngLat),
aroundLngLat,
around: endViewport.project(aroundPosition),
aroundPosition,
width: endProps.width,
height: endProps.height
});
Expand All @@ -53,16 +53,17 @@ export default class LinearInterpolator extends TransitionInterpolator {
propsInTransition[key] = lerp(startProps[key] || 0, endProps[key] || 0, t);
}

if (endProps.aroundLngLat) {
if (endProps.aroundPosition) {
// Linear transition should be performed in common space
const viewport = this.opts.makeViewport({...endProps, ...propsInTransition});
const [longitude, latitude] = viewport.getMapCenterByLngLatPosition({
lngLat: endProps.aroundLngLat,
// anchor point in current screen coordinates
pos: lerp(startProps.around, endProps.around, t)
});
propsInTransition.longitude = longitude;
propsInTransition.latitude = latitude;
Object.assign(
propsInTransition,
viewport.panByPosition(
endProps.aroundPosition,
// anchor point in current screen coordinates
lerp(startProps.around, endProps.around, t)
)
);
}
return propsInTransition;
}
Expand Down
12 changes: 6 additions & 6 deletions modules/core/src/viewports/globe-viewport.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,12 +168,12 @@ export default class GlobeViewport extends Viewport {
return xyz;
}

getMapCenterByLngLatPosition({lngLat, pos}) {
const fromPosition = this.unproject(pos);
return [
lngLat[0] - fromPosition[0] + this.longitude,
lngLat[1] - fromPosition[1] + this.latitude
];
panByPosition(coords, pixel) {
const fromPosition = this.unproject(pixel);
return {
longitude: coords[0] - fromPosition[0] + this.longitude,
latitude: coords[1] - fromPosition[1] + this.latitude
};
}
}

Expand Down
12 changes: 12 additions & 0 deletions modules/core/src/viewports/viewport.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,18 @@ export default class Viewport {

// EXPERIMENTAL METHODS

/**
* Needed by panning and linear transition
* Pan the viewport to place a given world coordinate at screen point [x, y]
*
* @param {Array} coords - world coordinates
* @param {Array} pixel - [x,y] coordinates on screen
* @return {Object} props of the new viewport
*/
panByPosition(coords, pixel) {
return null;
}

getCameraPosition() {
return this.cameraPosition;
}
Expand Down
19 changes: 5 additions & 14 deletions modules/core/src/viewports/web-mercator-viewport.js
Original file line number Diff line number Diff line change
Expand Up @@ -167,24 +167,15 @@ export default class WebMercatorViewport extends Viewport {
return addMetersToLngLat(lngLatZ, xyz);
}

/**
* Get the map center that place a given [lng, lat] coordinate at screen
* point [x, y]
*
* @param {Array} lngLat - [lng,lat] coordinates
* Specifies a point on the sphere.
* @param {Array} pos - [x,y] coordinates
* Specifies a point on the screen.
* @return {Array} [lng,lat] new map center.
*/
getMapCenterByLngLatPosition({lngLat, pos}) {
const fromLocation = pixelsToWorld(pos, this.pixelUnprojectionMatrix);
const toLocation = this.projectFlat(lngLat);
panByPosition(coords, pixel) {
const fromLocation = pixelsToWorld(pixel, this.pixelUnprojectionMatrix);
const toLocation = this.projectFlat(coords);

const translate = vec2.add([], toLocation, vec2.negate([], fromLocation));
const newCenter = vec2.add([], this.center, translate);

return this.unprojectFlat(newCenter);
const [longitude, latitude] = this.unprojectFlat(newCenter);
return {longitude, latitude};
}

getBounds(options = {}) {
Expand Down
Loading

0 comments on commit 743e230

Please sign in to comment.