aboutsummaryrefslogtreecommitdiff
path: root/src/client/views/nodes/MapBox/AnimationUtility.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/client/views/nodes/MapBox/AnimationUtility.ts')
-rw-r--r--src/client/views/nodes/MapBox/AnimationUtility.ts141
1 files changed, 108 insertions, 33 deletions
diff --git a/src/client/views/nodes/MapBox/AnimationUtility.ts b/src/client/views/nodes/MapBox/AnimationUtility.ts
index 13ce5b7e2..a5cff4668 100644
--- a/src/client/views/nodes/MapBox/AnimationUtility.ts
+++ b/src/client/views/nodes/MapBox/AnimationUtility.ts
@@ -7,7 +7,7 @@ import * as turf from '@turf/turf';
import { Position } from '@turf/turf';
import { Feature, FeatureCollection, GeoJsonProperties, Geometry } from 'geojson';
import { observer } from 'mobx-react';
-import { action, computed, observable } from 'mobx';
+import { action, computed, observable, runInAction } from 'mobx';
export enum AnimationStatus {
START = 'start',
@@ -24,18 +24,29 @@ export enum AnimationSpeed {
export class AnimationUtility {
private SMOOTH_FACTOR = 0.95;
private ROUTE_COORDINATES: Position[] = [];
+
+ @observable
private PATH: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties>;
+
+ private PATH_DISTANCE: number;
private FLY_IN_START_PITCH = 40;
private FIRST_LNG_LAT: { lng: number; lat: number };
private START_ALTITUDE = 3_000_000;
+ private MAP_REF: MapRef | null;
@observable private isStreetViewAnimation: boolean;
@observable private animationSpeed: AnimationSpeed;
+ @observable
private previousLngLat: { lng: number; lat: number };
+ private previousAltitude: number | null = null;
+ private previousPitch: number | null = null;
+
private currentStreetViewBearing: number = 0;
+ private terrainDisplayed: boolean;
+
@computed get flyInEndBearing() {
return this.isStreetViewAnimation
? this.calculateBearing(
@@ -51,36 +62,99 @@ export class AnimationUtility {
: -20;
}
+ @computed get currentAnimationAltitude(): number {
+ if (!this.isStreetViewAnimation) return 20_000;
+ if (!this.terrainDisplayed) return 50;
+ const coords: mapboxgl.LngLatLike = [this.previousLngLat.lng, this.previousLngLat.lat];
+ // console.log('MAP REF: ', this.MAP_REF)
+ // console.log("current elevation: ", this.MAP_REF?.queryTerrainElevation(coords));
+ let altitude = this.MAP_REF ? this.MAP_REF.queryTerrainElevation(coords) ?? 0 : 0;
+ if (altitude === 0) {
+ altitude += 50;
+ }
+ if (this.previousAltitude) {
+ return this.lerp(altitude, this.previousAltitude, 0.02);
+ }
+ return altitude;
+ }
+
@computed get flyInStartBearing() {
return Math.max(0, Math.min(this.flyInEndBearing + 20, 360)); // between 0 and 360
}
@computed get flyInEndAltitude() {
- return this.isStreetViewAnimation ? 70 : 10000;
+ // return this.isStreetViewAnimation ? (this.currentAnimationAltitude + 70 ): 10_000;
+ return this.currentAnimationAltitude;
+ }
+
+ @computed get currentPitch(): number {
+ if (!this.isStreetViewAnimation) return 50;
+ if (!this.terrainDisplayed) return 80;
+ else {
+ // const groundElevation = 0;
+ const heightAboveGround = this.currentAnimationAltitude;
+ const horizontalDistance = 500;
+
+ let pitch;
+ if (heightAboveGround >= 0) {
+ pitch = 90 - Math.atan(heightAboveGround / horizontalDistance) * (180 / Math.PI);
+ } else {
+ pitch = 80;
+ }
+
+ console.log(Math.max(50, Math.min(pitch, 85)));
+
+ if (this.previousPitch) {
+ return this.lerp(Math.max(50, Math.min(pitch, 85)), this.previousPitch, 0.02);
+ }
+ return Math.max(50, Math.min(pitch, 85));
+ }
}
@computed get flyInEndPitch() {
- return this.isStreetViewAnimation ? 80 : 50;
+ return this.currentPitch;
}
@computed get flyToDuration() {
switch (this.animationSpeed) {
case AnimationSpeed.SLOW:
- return 4000;
+ return 4_000;
case AnimationSpeed.MEDIUM:
- return 2500;
+ return 2_500;
case AnimationSpeed.FAST:
- return 1250;
+ return 1_250;
default:
- return 2500;
+ return 2_500;
}
}
@computed get animationDuration(): number {
- return 20_000;
- // compute path distance for a standard speed
- // get animation speed
- // get isStreetViewAnimation (should be slower if so)
+ let scalingFactor: number;
+ const MIN_DISTANCE = 0;
+ const MAX_DISTANCE = 3_000; // anything greater than 3000 is just set to 1 when normalized
+ const MAX_DURATION = this.isStreetViewAnimation ? 120_000 : 60_000;
+
+ const normalizedDistance = Math.min(1, (this.PATH_DISTANCE - MIN_DISTANCE) / (MAX_DISTANCE - MIN_DISTANCE));
+ const easedDistance = d3.easeExpOut(Math.min(normalizedDistance, 1));
+
+ switch (this.animationSpeed) {
+ case AnimationSpeed.SLOW:
+ scalingFactor = 250;
+ break;
+ case AnimationSpeed.MEDIUM:
+ scalingFactor = 150;
+ break;
+ case AnimationSpeed.FAST:
+ scalingFactor = 85;
+ break;
+ default:
+ scalingFactor = 150;
+ break;
+ }
+
+ const duration = Math.min(MAX_DURATION, easedDistance * MAX_DISTANCE * (this.isStreetViewAnimation ? scalingFactor * 1.12 : scalingFactor));
+
+ return duration;
}
@action
@@ -94,11 +168,21 @@ export class AnimationUtility {
this.isStreetViewAnimation = isStreetViewAnimation;
}
- constructor(firstLngLat: { lng: number; lat: number }, routeCoordinates: Position[], isStreetViewAnimation: boolean, animationSpeed: AnimationSpeed) {
+ @action
+ public setPath = (path: turf.helpers.Feature<turf.helpers.LineString, turf.helpers.Properties>) => {
+ this.PATH = path;
+ };
+
+ constructor(firstLngLat: { lng: number; lat: number }, routeCoordinates: Position[], isStreetViewAnimation: boolean, animationSpeed: AnimationSpeed, terrainDisplayed: boolean, mapRef: MapRef | null) {
this.FIRST_LNG_LAT = firstLngLat;
this.previousLngLat = firstLngLat;
+ this.isStreetViewAnimation = isStreetViewAnimation;
+ this.MAP_REF = mapRef;
+
this.ROUTE_COORDINATES = routeCoordinates;
this.PATH = turf.lineString(routeCoordinates);
+ this.PATH_DISTANCE = turf.lineDistance(this.PATH);
+ this.terrainDisplayed = terrainDisplayed;
const bearing = this.calculateBearing(
{
@@ -111,13 +195,7 @@ export class AnimationUtility {
}
);
this.currentStreetViewBearing = bearing;
- // if (isStreetViewAnimation){
- // this.flyInEndBearing = bearing;
- // }
- this.isStreetViewAnimation = isStreetViewAnimation;
this.animationSpeed = animationSpeed;
- // calculate animation duration based on speed
- // this.animationDuration = animationDuration;
}
public animatePath = async ({
@@ -140,8 +218,6 @@ export class AnimationUtility {
updateFrameId: (newFrameId: number) => void;
}) => {
return new Promise<void>(async resolve => {
- const pathDistance = turf.lineDistance(this.PATH);
- console.log('PATH DISTANCE: ', pathDistance);
let startTime: number | null = null;
const frame = async (currentTime: number) => {
@@ -157,7 +233,7 @@ export class AnimationUtility {
}
// calculate the distance along the path based on the animationPhase
- const alongPath = turf.along(this.PATH, pathDistance * animationPhase).geometry.coordinates;
+ const alongPath = turf.along(this.PATH, this.PATH_DISTANCE * animationPhase).geometry.coordinates;
const lngLat = {
lng: alongPath[0],
@@ -166,11 +242,7 @@ export class AnimationUtility {
let bearing: number;
if (this.isStreetViewAnimation) {
- bearing = this.lerp(
- this.currentStreetViewBearing,
- this.calculateBearing(this.previousLngLat, lngLat),
- 0.028 // Adjust the transition speed as needed
- );
+ bearing = this.lerp(this.currentStreetViewBearing, this.calculateBearing(this.previousLngLat, lngLat), 0.032);
this.currentStreetViewBearing = bearing;
// bearing = this.calculateBearing(this.previousLngLat, lngLat); // TODO: Calculate actual bearing
} else {
@@ -179,32 +251,35 @@ export class AnimationUtility {
// bearing = startBearing - animationPhase * 200.0;
}
- this.previousLngLat = lngLat;
+ runInAction(() => {
+ this.previousLngLat = lngLat;
+ });
updateAnimationPhase(animationPhase);
// compute corrected camera ground position, so that he leading edge of the path is in view
var correctedPosition = this.computeCameraPosition(
this.isStreetViewAnimation,
- this.flyInEndPitch,
+ this.currentPitch,
bearing,
lngLat,
- this.flyInEndAltitude,
+ this.currentAnimationAltitude,
true // smooth
);
// set the pitch and bearing of the camera
const camera = map.getFreeCameraOptions();
- camera.setPitchBearing(this.flyInEndPitch, bearing);
+ camera.setPitchBearing(this.currentPitch, bearing);
- console.log('Corrected pos: ', correctedPosition);
- console.log('Start altitude: ', this.flyInEndAltitude);
// set the position and altitude of the camera
- camera.position = MercatorCoordinate.fromLngLat(correctedPosition, this.flyInEndAltitude);
+ camera.position = MercatorCoordinate.fromLngLat(correctedPosition, this.currentAnimationAltitude);
// apply the new camera options
map.setFreeCameraOptions(camera);
+ this.previousAltitude = this.currentAnimationAltitude;
+ this.previousPitch = this.previousPitch;
+
// repeat!
const innerFrameId = await window.requestAnimationFrame(frame);
updateFrameId(innerFrameId);