| 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;
 |