Skip to content

Commit

Permalink
Merge branch 'master' into felix/deck-layer-parameters-v9
Browse files Browse the repository at this point in the history
  • Loading branch information
felixpalmer committed Mar 14, 2024
2 parents 5ff84d0 + 19d3018 commit c24ab11
Show file tree
Hide file tree
Showing 32 changed files with 1,049 additions and 1,235 deletions.
9 changes: 5 additions & 4 deletions docs/api-reference/core/first-person-controller.md
Expand Up @@ -4,7 +4,7 @@ Inherits from [Base Controller](./controller.md).

The `FirstPersonController` class can be passed to either the `Deck` class's [controller](./deck.md#controller) prop or a `View` class's [controller](./view.md#controller) prop to specify that viewport interaction should be enabled.

`FirstPersonController` is the default controller for [FirstPersonView](./first-person-view.md).
`FirstPersonController` is the default controller for [FirstPersonView](./first-person-view.md). It simulates the movement of a human being, with the scroll motion moving forward/backwards and dragging rotating the head.

## Usage

Expand Down Expand Up @@ -37,9 +37,10 @@ new Deck({

Supports all [Controller options](./controller.md#options) with the following default behavior:

- `dragMode`: default `'rotate'` (drag to rotate)
- `dragPan`: not effective, this view does not support panning
- `keyboard`: arrow keys to move camera, arrow keys with shift/ctrl down to rotate, +/- to zoom
- `dragMode`: default `'rotate'` (drag to rotate, shift-drag to pan)
- `dragPan`: default `true` (supported only from v9.0)
- `keyboard`: arrow keys to move camera, arrow keys with shift/ctrl down to rotate, +/- to move vertically
- `scrollZoom`: scroll to move in direction of mouse pointer, in horizontal 2D plane


## Custom FirstPersonController
Expand Down
2 changes: 1 addition & 1 deletion examples/README.md
Expand Up @@ -14,7 +14,7 @@ package.json, bundler config etc) examples of how to get deck.gl and a base
map working together.

* **[Pure JS](./get-started/pure-js)** Applications without depending any additional framework. Bundled and served with [Vite](https://vitejs.dev).
* **[React](./get-started/react)** React exmples using `@deck.gl/react` and `react-map-gl`. Bundled and served with [Vite](https://vitejs.dev).
* **[React](./get-started/react)** React examples using `@deck.gl/react` and `react-map-gl`. Bundled and served with [Vite](https://vitejs.dev).
* **[Scripting](./get-started/scripting)** HTML single-file examples that can be opened directly in a browser.


Expand Down
4 changes: 2 additions & 2 deletions modules/carto/src/layers/vector-tile-layer.ts
Expand Up @@ -16,7 +16,7 @@ import {
import {GeoJsonLayer} from '@deck.gl/layers';
import {binaryToGeojson} from '@loaders.gl/gis';
import type {BinaryFeatureCollection} from '@loaders.gl/schema';
import type {Feature} from 'geojson';
import type {Feature, Geometry} from 'geojson';

import type {TilejsonResult} from '../sources/types';
import {TilejsonPropType, injectAccessToken, mergeBoundaryData} from './utils';
Expand Down Expand Up @@ -153,7 +153,7 @@ export default class VectorTileLayer<
const {data} = params.sourceLayer!.props;
info.object = binaryToGeojson(data as BinaryFeatureCollection, {
globalFeatureId: info.index
}) as Feature;
}) as Feature<Geometry, FeaturePropertiesT>;
}

return info;
Expand Down
92 changes: 72 additions & 20 deletions modules/core/src/controllers/first-person-controller.ts
@@ -1,11 +1,13 @@
import Controller from './controller';
import ViewState from './view-state';
import {mod} from '../utils/math-utils';
import type Viewport from '../viewports/viewport';
import LinearInterpolator from '../transitions/linear-interpolator';

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

const MOVEMENT_SPEED = 20;
const PAN_SPEED = 500;

type FirstPersonStateProps = {
width: number;
Expand All @@ -28,14 +30,23 @@ type FirstPersonStateInternal = {
startBearing?: number;
startPitch?: number;
startZoomPosition?: number[];
startPanPos?: [number, number];
startPanPosition?: number[];
};

class FirstPersonState extends ViewState<
FirstPersonState,
FirstPersonStateProps,
FirstPersonStateInternal
> {
constructor(options: FirstPersonStateProps & FirstPersonStateInternal) {
makeViewport: (props: Record<string, any>) => Viewport;

constructor(
options: FirstPersonStateProps &
FirstPersonStateInternal & {
makeViewport: (props: Record<string, any>) => Viewport;
}
) {
const {
/* Viewport arguments */
width, // Width of viewport
Expand All @@ -58,7 +69,9 @@ class FirstPersonState extends ViewState<
startRotatePos,
startBearing,
startPitch,
startZoomPosition
startZoomPosition,
startPanPos,
startPanPosition
} = options;

super(
Expand All @@ -77,9 +90,13 @@ class FirstPersonState extends ViewState<
startRotatePos,
startBearing,
startPitch,
startZoomPosition
startZoomPosition,
startPanPos,
startPanPosition
}
);

this.makeViewport = options.makeViewport;
}

/* Public API */
Expand All @@ -88,24 +105,48 @@ class FirstPersonState extends ViewState<
* Start panning
* @param {[Number, Number]} pos - position on screen where the pointer grabs
*/
panStart(): FirstPersonState {
return this;
panStart({pos}): FirstPersonState {
const {position} = this.getViewportProps();
return this._getUpdatedState({
startPanPos: pos,
startPanPosition: position
});
}

/**
* Pan
* @param {[Number, Number]} pos - position on screen where the pointer is
*/
pan(): FirstPersonState {
return this;
pan({pos}): FirstPersonState {
if (!pos) {
return this;
}
const {startPanPos = [0, 0], startPanPosition = [0, 0]} = this.getState();
const {width, height, bearing, pitch} = this.getViewportProps();
const deltaScaleX = (PAN_SPEED * (pos[0] - startPanPos[0])) / width;
const deltaScaleY = (PAN_SPEED * (pos[1] - startPanPos[1])) / height;

const up = new SphericalCoordinates({bearing, pitch});
const forward = new SphericalCoordinates({bearing, pitch: -90});
const yDirection = up.toVector3().normalize();
const xDirection = forward.toVector3().cross(yDirection).normalize();

return this._getUpdatedState({
position: new Vector3(startPanPosition)
.add(xDirection.scale(deltaScaleX))
.add(yDirection.scale(deltaScaleY))
});
}

/**
* End panning
* Must call if `panStart()` was called
*/
panEnd(): FirstPersonState {
return this;
return this._getUpdatedState({
startPanPos: null,
startPanPosition: null
});
}

/**
Expand Down Expand Up @@ -188,14 +229,20 @@ class FirstPersonState extends ViewState<
* @param {Number} scale - a number between [0, 1] specifying the accumulated
* relative scale.
*/
zoom({scale}: {scale: number}): FirstPersonState {
let {startZoomPosition} = this.getState();
if (!startZoomPosition) {
startZoomPosition = this.getViewportProps().position;
}
zoom({pos, scale}: {pos: [number, number]; scale: number}): FirstPersonState {
const viewportProps = this.getViewportProps();
const startZoomPosition = this.getState().startZoomPosition || viewportProps.position;
const viewport = this.makeViewport(viewportProps);
const {projectionMatrix, width} = viewport;
const fovxRadians = 2.0 * Math.atan(1.0 / projectionMatrix[0]);
const angle = fovxRadians * (pos[0] / width - 0.5);

const direction = this.getDirection();
return this._move(direction, Math.log2(scale) * MOVEMENT_SPEED, startZoomPosition);
const direction = this.getDirection(true);
return this._move(
direction.rotateZ({radians: -angle}),
Math.log2(scale) * MOVEMENT_SPEED,
startZoomPosition
);
}

/**
Expand Down Expand Up @@ -254,12 +301,12 @@ class FirstPersonState extends ViewState<
});
}

zoomIn(speed: number = 2): FirstPersonState {
return this.zoom({scale: speed});
zoomIn(speed: number = MOVEMENT_SPEED): FirstPersonState {
return this._move(new Vector3(0, 0, 1), speed);
}

zoomOut(speed: number = 2): FirstPersonState {
return this.zoom({scale: 1 / speed});
zoomOut(speed: number = MOVEMENT_SPEED): FirstPersonState {
return this._move(new Vector3(0, 0, -1), speed);
}

// shortest path between two view states
Expand Down Expand Up @@ -304,7 +351,12 @@ class FirstPersonState extends ViewState<

_getUpdatedState(newProps: Record<string, any>): FirstPersonState {
// Update _viewportProps
return new FirstPersonState({...this.getViewportProps(), ...this.getState(), ...newProps});
return new FirstPersonState({
makeViewport: this.makeViewport,
...this.getViewportProps(),
...this.getState(),
...newProps
});
}

// Apply any constraints (mathematical or defined by _viewportProps) to map state
Expand Down
2 changes: 1 addition & 1 deletion modules/core/src/lib/attribute/attribute-manager.ts
Expand Up @@ -275,7 +275,7 @@ export default class AttributeManager {
* @return {Object} attributes - descriptors
*/
getAttributes(): {[id: string]: Attribute} {
return this.attributes;
return {...this.attributes, ...this.attributeTransitionManager.getAttributes()};
}

/**
Expand Down
Expand Up @@ -6,10 +6,10 @@ import log from '../../utils/log';

import type {Device} from '@luma.gl/core';
import type {Timeline} from '@luma.gl/engine';
import type GPUTransition from '../../transitions/gpu-transition';
import type {GPUTransition} from '../../transitions/gpu-transition';
import type {ConstructorOf} from '../../types/types';
import type Attribute from './attribute';
import type {TransitionSettings} from './attribute-transition-utils';
import type {TransitionSettings} from './transition-settings';

const TRANSITION_TYPES: Record<string, ConstructorOf<GPUTransition>> = {
interpolation: GPUInterpolationTransition,
Expand Down Expand Up @@ -129,7 +129,7 @@ export default class AttributeTransitionManager {

/* Private methods */
private _removeTransition(attributeName: string): void {
this.transitions[attributeName].cancel();
this.transitions[attributeName].delete();
delete this.transitions[attributeName];
}

Expand Down

0 comments on commit c24ab11

Please sign in to comment.