123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576 |
- import Cartesian2 from "../Core/Cartesian2.js";
- import Cartesian3 from "../Core/Cartesian3.js";
- import Cartographic from "../Core/Cartographic.js";
- import defaultValue from "../Core/defaultValue.js";
- import defined from "../Core/defined.js";
- import DeveloperError from "../Core/DeveloperError.js";
- import EasingFunction from "../Core/EasingFunction.js";
- import CesiumMath from "../Core/Math.js";
- import PerspectiveFrustum from "../Core/PerspectiveFrustum.js";
- import PerspectiveOffCenterFrustum from "../Core/PerspectiveOffCenterFrustum.js";
- import SceneMode from "./SceneMode.js";
- /**
- * Creates tweens for camera flights.
- * <br /><br />
- * Mouse interaction is disabled during flights.
- *
- * @private
- */
- const CameraFlightPath = {};
- function getAltitude(frustum, dx, dy) {
- let near;
- let top;
- let right;
- if (frustum instanceof PerspectiveFrustum) {
- const tanTheta = Math.tan(0.5 * frustum.fovy);
- near = frustum.near;
- top = frustum.near * tanTheta;
- right = frustum.aspectRatio * top;
- return Math.max((dx * near) / right, (dy * near) / top);
- } else if (frustum instanceof PerspectiveOffCenterFrustum) {
- near = frustum.near;
- top = frustum.top;
- right = frustum.right;
- return Math.max((dx * near) / right, (dy * near) / top);
- }
- return Math.max(dx, dy);
- }
- const scratchCart = new Cartesian3();
- const scratchCart2 = new Cartesian3();
- function createPitchFunction(
- startPitch,
- endPitch,
- heightFunction,
- pitchAdjustHeight
- ) {
- if (defined(pitchAdjustHeight) && heightFunction(0.5) > pitchAdjustHeight) {
- const startHeight = heightFunction(0.0);
- const endHeight = heightFunction(1.0);
- const middleHeight = heightFunction(0.5);
- const d1 = middleHeight - startHeight;
- const d2 = middleHeight - endHeight;
- return function (time) {
- const altitude = heightFunction(time);
- if (time <= 0.5) {
- const t1 = (altitude - startHeight) / d1;
- return CesiumMath.lerp(startPitch, -CesiumMath.PI_OVER_TWO, t1);
- }
- const t2 = (altitude - endHeight) / d2;
- return CesiumMath.lerp(-CesiumMath.PI_OVER_TWO, endPitch, 1 - t2);
- };
- }
- return function (time) {
- return CesiumMath.lerp(startPitch, endPitch, time);
- };
- }
- function createHeightFunction(
- camera,
- destination,
- startHeight,
- endHeight,
- optionAltitude
- ) {
- let altitude = optionAltitude;
- const maxHeight = Math.max(startHeight, endHeight);
- if (!defined(altitude)) {
- const start = camera.position;
- const end = destination;
- const up = camera.up;
- const right = camera.right;
- const frustum = camera.frustum;
- const diff = Cartesian3.subtract(start, end, scratchCart);
- const verticalDistance = Cartesian3.magnitude(
- Cartesian3.multiplyByScalar(up, Cartesian3.dot(diff, up), scratchCart2)
- );
- const horizontalDistance = Cartesian3.magnitude(
- Cartesian3.multiplyByScalar(
- right,
- Cartesian3.dot(diff, right),
- scratchCart2
- )
- );
- altitude = Math.min(
- getAltitude(frustum, verticalDistance, horizontalDistance) * 0.2,
- 1000000000.0
- );
- }
- if (maxHeight < altitude) {
- const power = 8.0;
- const factor = 1000000.0;
- const s = -Math.pow((altitude - startHeight) * factor, 1.0 / power);
- const e = Math.pow((altitude - endHeight) * factor, 1.0 / power);
- return function (t) {
- const x = t * (e - s) + s;
- return -Math.pow(x, power) / factor + altitude;
- };
- }
- return function (t) {
- return CesiumMath.lerp(startHeight, endHeight, t);
- };
- }
- function adjustAngleForLERP(startAngle, endAngle) {
- if (
- CesiumMath.equalsEpsilon(
- startAngle,
- CesiumMath.TWO_PI,
- CesiumMath.EPSILON11
- )
- ) {
- startAngle = 0.0;
- }
- if (endAngle > startAngle + Math.PI) {
- startAngle += CesiumMath.TWO_PI;
- } else if (endAngle < startAngle - Math.PI) {
- startAngle -= CesiumMath.TWO_PI;
- }
- return startAngle;
- }
- const scratchStart = new Cartesian3();
- function createUpdateCV(
- scene,
- duration,
- destination,
- heading,
- pitch,
- roll,
- optionAltitude,
- optionPitchAdjustHeight
- ) {
- const camera = scene.camera;
- const start = Cartesian3.clone(camera.position, scratchStart);
- const startPitch = camera.pitch;
- const startHeading = adjustAngleForLERP(camera.heading, heading);
- const startRoll = adjustAngleForLERP(camera.roll, roll);
- const heightFunction = createHeightFunction(
- camera,
- destination,
- start.z,
- destination.z,
- optionAltitude
- );
- const pitchFunction = createPitchFunction(
- startPitch,
- pitch,
- heightFunction,
- optionPitchAdjustHeight
- );
- function update(value) {
- const time = value.time / duration;
- camera.setView({
- orientation: {
- heading: CesiumMath.lerp(startHeading, heading, time),
- pitch: pitchFunction(time),
- roll: CesiumMath.lerp(startRoll, roll, time),
- },
- });
- Cartesian2.lerp(start, destination, time, camera.position);
- camera.position.z = heightFunction(time);
- }
- return update;
- }
- function useLongestFlight(startCart, destCart) {
- if (startCart.longitude < destCart.longitude) {
- startCart.longitude += CesiumMath.TWO_PI;
- } else {
- destCart.longitude += CesiumMath.TWO_PI;
- }
- }
- function useShortestFlight(startCart, destCart) {
- const diff = startCart.longitude - destCart.longitude;
- if (diff < -CesiumMath.PI) {
- startCart.longitude += CesiumMath.TWO_PI;
- } else if (diff > CesiumMath.PI) {
- destCart.longitude += CesiumMath.TWO_PI;
- }
- }
- const scratchStartCart = new Cartographic();
- const scratchEndCart = new Cartographic();
- function createUpdate3D(
- scene,
- duration,
- destination,
- heading,
- pitch,
- roll,
- optionAltitude,
- optionFlyOverLongitude,
- optionFlyOverLongitudeWeight,
- optionPitchAdjustHeight
- ) {
- const camera = scene.camera;
- const projection = scene.mapProjection;
- const ellipsoid = projection.ellipsoid;
- const startCart = Cartographic.clone(
- camera.positionCartographic,
- scratchStartCart
- );
- const startPitch = camera.pitch;
- const startHeading = adjustAngleForLERP(camera.heading, heading);
- const startRoll = adjustAngleForLERP(camera.roll, roll);
- const destCart = ellipsoid.cartesianToCartographic(
- destination,
- scratchEndCart
- );
- startCart.longitude = CesiumMath.zeroToTwoPi(startCart.longitude);
- destCart.longitude = CesiumMath.zeroToTwoPi(destCart.longitude);
- let useLongFlight = false;
- if (defined(optionFlyOverLongitude)) {
- const hitLon = CesiumMath.zeroToTwoPi(optionFlyOverLongitude);
- const lonMin = Math.min(startCart.longitude, destCart.longitude);
- const lonMax = Math.max(startCart.longitude, destCart.longitude);
- const hitInside = hitLon >= lonMin && hitLon <= lonMax;
- if (defined(optionFlyOverLongitudeWeight)) {
- // Distance inside (0...2Pi)
- const din = Math.abs(startCart.longitude - destCart.longitude);
- // Distance outside (0...2Pi)
- const dot = CesiumMath.TWO_PI - din;
- const hitDistance = hitInside ? din : dot;
- const offDistance = hitInside ? dot : din;
- if (
- hitDistance < offDistance * optionFlyOverLongitudeWeight &&
- !hitInside
- ) {
- useLongFlight = true;
- }
- } else if (!hitInside) {
- useLongFlight = true;
- }
- }
- if (useLongFlight) {
- useLongestFlight(startCart, destCart);
- } else {
- useShortestFlight(startCart, destCart);
- }
- const heightFunction = createHeightFunction(
- camera,
- destination,
- startCart.height,
- destCart.height,
- optionAltitude
- );
- const pitchFunction = createPitchFunction(
- startPitch,
- pitch,
- heightFunction,
- optionPitchAdjustHeight
- );
- // Isolate scope for update function.
- // to have local copies of vars used in lerp
- // Othervise, if you call nex
- // createUpdate3D (createAnimationTween)
- // before you played animation, variables will be overwriten.
- function isolateUpdateFunction() {
- const startLongitude = startCart.longitude;
- const destLongitude = destCart.longitude;
- const startLatitude = startCart.latitude;
- const destLatitude = destCart.latitude;
- return function update(value) {
- const time = value.time / duration;
- const position = Cartesian3.fromRadians(
- CesiumMath.lerp(startLongitude, destLongitude, time),
- CesiumMath.lerp(startLatitude, destLatitude, time),
- heightFunction(time),
- ellipsoid
- );
- camera.setView({
- destination: position,
- orientation: {
- heading: CesiumMath.lerp(startHeading, heading, time),
- pitch: pitchFunction(time),
- roll: CesiumMath.lerp(startRoll, roll, time),
- },
- });
- };
- }
- return isolateUpdateFunction();
- }
- function createUpdate2D(
- scene,
- duration,
- destination,
- heading,
- pitch,
- roll,
- optionAltitude
- ) {
- const camera = scene.camera;
- const start = Cartesian3.clone(camera.position, scratchStart);
- const startHeading = adjustAngleForLERP(camera.heading, heading);
- const startHeight = camera.frustum.right - camera.frustum.left;
- const heightFunction = createHeightFunction(
- camera,
- destination,
- startHeight,
- destination.z,
- optionAltitude
- );
- function update(value) {
- const time = value.time / duration;
- camera.setView({
- orientation: {
- heading: CesiumMath.lerp(startHeading, heading, time),
- },
- });
- Cartesian2.lerp(start, destination, time, camera.position);
- const zoom = heightFunction(time);
- const frustum = camera.frustum;
- const ratio = frustum.top / frustum.right;
- const incrementAmount = (zoom - (frustum.right - frustum.left)) * 0.5;
- frustum.right += incrementAmount;
- frustum.left -= incrementAmount;
- frustum.top = ratio * frustum.right;
- frustum.bottom = -frustum.top;
- }
- return update;
- }
- const scratchCartographic = new Cartographic();
- const scratchDestination = new Cartesian3();
- function emptyFlight(complete, cancel) {
- return {
- startObject: {},
- stopObject: {},
- duration: 0.0,
- complete: complete,
- cancel: cancel,
- };
- }
- function wrapCallback(controller, cb) {
- function wrapped() {
- if (typeof cb === "function") {
- cb();
- }
- controller.enableInputs = true;
- }
- return wrapped;
- }
- CameraFlightPath.createTween = function (scene, options) {
- options = defaultValue(options, defaultValue.EMPTY_OBJECT);
- let destination = options.destination;
- //>>includeStart('debug', pragmas.debug);
- if (!defined(scene)) {
- throw new DeveloperError("scene is required.");
- }
- if (!defined(destination)) {
- throw new DeveloperError("destination is required.");
- }
- //>>includeEnd('debug');
- const mode = scene.mode;
- if (mode === SceneMode.MORPHING) {
- return emptyFlight();
- }
- const convert = defaultValue(options.convert, true);
- const projection = scene.mapProjection;
- const ellipsoid = projection.ellipsoid;
- const maximumHeight = options.maximumHeight;
- const flyOverLongitude = options.flyOverLongitude;
- const flyOverLongitudeWeight = options.flyOverLongitudeWeight;
- const pitchAdjustHeight = options.pitchAdjustHeight;
- let easingFunction = options.easingFunction;
- if (convert && mode !== SceneMode.SCENE3D) {
- ellipsoid.cartesianToCartographic(destination, scratchCartographic);
- destination = projection.project(scratchCartographic, scratchDestination);
- }
- const camera = scene.camera;
- const transform = options.endTransform;
- if (defined(transform)) {
- camera._setTransform(transform);
- }
- let duration = options.duration;
- if (!defined(duration)) {
- duration =
- Math.ceil(Cartesian3.distance(camera.position, destination) / 1000000.0) +
- 2.0;
- duration = Math.min(duration, 3.0);
- }
- const heading = defaultValue(options.heading, 0.0);
- const pitch = defaultValue(options.pitch, -CesiumMath.PI_OVER_TWO);
- const roll = defaultValue(options.roll, 0.0);
- const controller = scene.screenSpaceCameraController;
- controller.enableInputs = false;
- const complete = wrapCallback(controller, options.complete);
- const cancel = wrapCallback(controller, options.cancel);
- const frustum = camera.frustum;
- let empty = scene.mode === SceneMode.SCENE2D;
- empty =
- empty &&
- Cartesian2.equalsEpsilon(camera.position, destination, CesiumMath.EPSILON6);
- empty =
- empty &&
- CesiumMath.equalsEpsilon(
- Math.max(frustum.right - frustum.left, frustum.top - frustum.bottom),
- destination.z,
- CesiumMath.EPSILON6
- );
- empty =
- empty ||
- (scene.mode !== SceneMode.SCENE2D &&
- Cartesian3.equalsEpsilon(
- destination,
- camera.position,
- CesiumMath.EPSILON10
- ));
- empty =
- empty &&
- CesiumMath.equalsEpsilon(
- CesiumMath.negativePiToPi(heading),
- CesiumMath.negativePiToPi(camera.heading),
- CesiumMath.EPSILON10
- ) &&
- CesiumMath.equalsEpsilon(
- CesiumMath.negativePiToPi(pitch),
- CesiumMath.negativePiToPi(camera.pitch),
- CesiumMath.EPSILON10
- ) &&
- CesiumMath.equalsEpsilon(
- CesiumMath.negativePiToPi(roll),
- CesiumMath.negativePiToPi(camera.roll),
- CesiumMath.EPSILON10
- );
- if (empty) {
- return emptyFlight(complete, cancel);
- }
- const updateFunctions = new Array(4);
- updateFunctions[SceneMode.SCENE2D] = createUpdate2D;
- updateFunctions[SceneMode.SCENE3D] = createUpdate3D;
- updateFunctions[SceneMode.COLUMBUS_VIEW] = createUpdateCV;
- if (duration <= 0.0) {
- const newOnComplete = function () {
- const update = updateFunctions[mode](
- scene,
- 1.0,
- destination,
- heading,
- pitch,
- roll,
- maximumHeight,
- flyOverLongitude,
- flyOverLongitudeWeight,
- pitchAdjustHeight
- );
- update({ time: 1.0 });
- if (typeof complete === "function") {
- complete();
- }
- };
- return emptyFlight(newOnComplete, cancel);
- }
- const update = updateFunctions[mode](
- scene,
- duration,
- destination,
- heading,
- pitch,
- roll,
- maximumHeight,
- flyOverLongitude,
- flyOverLongitudeWeight,
- pitchAdjustHeight
- );
- if (!defined(easingFunction)) {
- const startHeight = camera.positionCartographic.height;
- const endHeight =
- mode === SceneMode.SCENE3D
- ? ellipsoid.cartesianToCartographic(destination).height
- : destination.z;
- if (startHeight > endHeight && startHeight > 11500.0) {
- easingFunction = EasingFunction.CUBIC_OUT;
- } else {
- easingFunction = EasingFunction.QUINTIC_IN_OUT;
- }
- }
- return {
- duration: duration,
- easingFunction: easingFunction,
- startObject: {
- time: 0.0,
- },
- stopObject: {
- time: duration,
- },
- update: update,
- complete: complete,
- cancel: cancel,
- };
- };
- export default CameraFlightPath;
|