diff options
Diffstat (limited to 'src/client/views/nodes/MapBox/AnimationUtility.ts')
-rw-r--r-- | src/client/views/nodes/MapBox/AnimationUtility.ts | 147 |
1 files changed, 114 insertions, 33 deletions
diff --git a/src/client/views/nodes/MapBox/AnimationUtility.ts b/src/client/views/nodes/MapBox/AnimationUtility.ts index 256acbf13..11b335a96 100644 --- a/src/client/views/nodes/MapBox/AnimationUtility.ts +++ b/src/client/views/nodes/MapBox/AnimationUtility.ts @@ -5,9 +5,8 @@ import * as React from 'react'; import * as d3 from "d3"; 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 { Feature, GeoJsonProperties, Geometry } from "geojson"; +import { action, computed, observable, runInAction } from "mobx"; export enum AnimationStatus { START = 'start', @@ -21,23 +20,32 @@ export enum AnimationSpeed { FAST = '3x', } -@observer 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,38 +59,103 @@ 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 @@ -96,17 +169,29 @@ export class AnimationUtility { this.isStreetViewAnimation = isStreetViewAnimation; } + @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 + 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( { @@ -119,13 +204,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 ({ @@ -151,8 +230,6 @@ export class AnimationUtility { }) => { 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) => { @@ -169,7 +246,7 @@ export class AnimationUtility { // calculate the distance along the path based on the animationPhase - const alongPath = turf.along(this.PATH, pathDistance * animationPhase).geometry + const alongPath = turf.along(this.PATH, this.PATH_DISTANCE * animationPhase).geometry .coordinates; const lngLat = { @@ -182,7 +259,7 @@ export class AnimationUtility { bearing = this.lerp( this.currentStreetViewBearing, this.calculateBearing(this.previousLngLat, lngLat), - 0.028 // Adjust the transition speed as needed + 0.032 ); this.currentStreetViewBearing = bearing; // bearing = this.calculateBearing(this.previousLngLat, lngLat); // TODO: Calculate actual bearing @@ -192,35 +269,39 @@ 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 + 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); |