text: import when from "../ThirdParty/when.js";
  import Cartesian2 from "./Cartesian2.js";
  import Cartesian3 from "./Cartesian3.js";
  import Cartesian4 from "./Cartesian4.js";
  import Cartographic from "./Cartographic.js";
  import Check from "./Check.js";
  import defaultValue from "./defaultValue.js";
  import defined from "./defined.js";
  import DeveloperError from "./DeveloperError.js";
  import EarthOrientationParameters from "./EarthOrientationParameters.js";
  import EarthOrientationParametersSample from "./EarthOrientationParametersSample.js";
  import Ellipsoid from "./Ellipsoid.js";
  import HeadingPitchRoll from "./HeadingPitchRoll.js";
  import Iau2006XysData from "./Iau2006XysData.js";
  import Iau2006XysSample from "./Iau2006XysSample.js";
  import JulianDate from "./JulianDate.js";
  import CesiumMath from "./Math.js";
  import Matrix3 from "./Matrix3.js";
  import Matrix4 from "./Matrix4.js";
  import Quaternion from "./Quaternion.js";
  import TimeConstants from "./TimeConstants.js";
  
  /**
   * Contains functions for transforming positions to various reference frames.
   *
   * @namespace Transforms
   */
  var Transforms = {};
  
  var vectorProductLocalFrame = {
    up: {
      south: "east",
      north: "west",
      west: "south",
      east: "north",
    },
    down: {
      south: "west",
      north: "east",
      west: "north",
      east: "south",
    },
    south: {
      up: "west",
      down: "east",
      west: "down",
      east: "up",
    },
    north: {
      up: "east",
      down: "west",
      west: "up",
      east: "down",
    },
    west: {
      up: "north",
      down: "south",
      north: "down",
      south: "up",
    },
    east: {
      up: "south",
      down: "north",
      north: "up",
      south: "down",
    },
  };
  
  var degeneratePositionLocalFrame = {
    north: [-1, 0, 0],
    east: [0, 1, 0],
    up: [0, 0, 1],
    south: [1, 0, 0],
    west: [0, -1, 0],
    down: [0, 0, -1],
  };
  
  var localFrameToFixedFrameCache = {};
  
  var scratchCalculateCartesian = {
    east: new Cartesian3(),
    north: new Cartesian3(),
    up: new Cartesian3(),
    west: new Cartesian3(),
    south: new Cartesian3(),
    down: new Cartesian3(),
  };
  var scratchFirstCartesian = new Cartesian3();
  var scratchSecondCartesian = new Cartesian3();
  var scratchThirdCartesian = new Cartesian3();
  /**
   * Generates a function that computes a 4x4 transformation matrix from a reference frame
   * centered at the provided origin to the provided ellipsoid's fixed reference frame.
   * @param  {String} firstAxis  name of the first axis of the local reference frame. Must be
   *  'east', 'north', 'up', 'west', 'south' or 'down'.
   * @param  {String} secondAxis  name of the second axis of the local reference frame. Must be
   *  'east', 'north', 'up', 'west', 'south' or 'down'.
   * @return {Transforms.LocalFrameToFixedFrame} The function that will computes a
   * 4x4 transformation matrix from a reference frame, with first axis and second axis compliant with the parameters,
   */
  Transforms.localFrameToFixedFrameGenerator = function (firstAxis, secondAxis) {
    if (
      !vectorProductLocalFrame.hasOwnProperty(firstAxis) ||
      !vectorProductLocalFrame[firstAxis].hasOwnProperty(secondAxis)
    ) {
      throw new DeveloperError(
        "firstAxis and secondAxis must be east, north, up, west, south or down."
      );
    }
    var thirdAxis = vectorProductLocalFrame[firstAxis][secondAxis];
  
    /**
     * Computes a 4x4 transformation matrix from a reference frame
     * centered at the provided origin to the provided ellipsoid's fixed reference frame.
     * @callback Transforms.LocalFrameToFixedFrame
     * @param {Cartesian3} origin The center point of the local reference frame.
     * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation.
     * @param {Matrix4} [result] The object onto which to store the result.
     * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided.
     */
    var resultat;
    var hashAxis = firstAxis + secondAxis;
    if (defined(localFrameToFixedFrameCache[hashAxis])) {
      resultat = localFrameToFixedFrameCache[hashAxis];
    } else {
      resultat = function (origin, ellipsoid, result) {
        //>>includeStart('debug', pragmas.debug);
        if (!defined(origin)) {
          throw new DeveloperError("origin is required.");
        }
        //>>includeEnd('debug');
        if (!defined(result)) {
          result = new Matrix4();
        }
        if (
          Cartesian3.equalsEpsilon(origin, Cartesian3.ZERO, CesiumMath.EPSILON14)
        ) {
          // If x, y, and z are zero, use the degenerate local frame, which is a special case
          Cartesian3.unpack(
            degeneratePositionLocalFrame[firstAxis],
            0,
            scratchFirstCartesian
          );
          Cartesian3.unpack(
            degeneratePositionLocalFrame[secondAxis],
            0,
            scratchSecondCartesian
          );
          Cartesian3.unpack(
            degeneratePositionLocalFrame[thirdAxis],
            0,
            scratchThirdCartesian
          );
        } else if (
          CesiumMath.equalsEpsilon(origin.x, 0.0, CesiumMath.EPSILON14) &&
          CesiumMath.equalsEpsilon(origin.y, 0.0, CesiumMath.EPSILON14)
        ) {
          // If x and y are zero, assume origin is at a pole, which is a special case.
          var sign = CesiumMath.sign(origin.z);
  
          Cartesian3.unpack(
            degeneratePositionLocalFrame[firstAxis],
            0,
            scratchFirstCartesian
          );
          if (firstAxis !== "east" && firstAxis !== "west") {
            Cartesian3.multiplyByScalar(
              scratchFirstCartesian,
              sign,
              scratchFirstCartesian
            );
          }
  
          Cartesian3.unpack(
            degeneratePositionLocalFrame[secondAxis],
            0,
            scratchSecondCartesian
          );
          if (secondAxis !== "east" && secondAxis !== "west") {
            Cartesian3.multiplyByScalar(
              scratchSecondCartesian,
              sign,
              scratchSecondCartesian
            );
          }
  
          Cartesian3.unpack(
            degeneratePositionLocalFrame[thirdAxis],
            0,
            scratchThirdCartesian
          );
          if (thirdAxis !== "east" && thirdAxis !== "west") {
            Cartesian3.multiplyByScalar(
              scratchThirdCartesian,
              sign,
              scratchThirdCartesian
            );
          }
        } else {
          ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
          ellipsoid.geodeticSurfaceNormal(origin, scratchCalculateCartesian.up);
  
          var up = scratchCalculateCartesian.up;
          var east = scratchCalculateCartesian.east;
          east.x = -origin.y;
          east.y = origin.x;
          east.z = 0.0;
          Cartesian3.normalize(east, scratchCalculateCartesian.east);
          Cartesian3.cross(up, east, scratchCalculateCartesian.north);
  
          Cartesian3.multiplyByScalar(
            scratchCalculateCartesian.up,
            -1,
            scratchCalculateCartesian.down
          );
          Cartesian3.multiplyByScalar(
            scratchCalculateCartesian.east,
            -1,
            scratchCalculateCartesian.west
          );
          Cartesian3.multiplyByScalar(
            scratchCalculateCartesian.north,
            -1,
            scratchCalculateCartesian.south
          );
  
          scratchFirstCartesian = scratchCalculateCartesian[firstAxis];
          scratchSecondCartesian = scratchCalculateCartesian[secondAxis];
          scratchThirdCartesian = scratchCalculateCartesian[thirdAxis];
        }
        result[0] = scratchFirstCartesian.x;
        result[1] = scratchFirstCartesian.y;
        result[2] = scratchFirstCartesian.z;
        result[3] = 0.0;
        result[4] = scratchSecondCartesian.x;
        result[5] = scratchSecondCartesian.y;
        result[6] = scratchSecondCartesian.z;
        result[7] = 0.0;
        result[8] = scratchThirdCartesian.x;
        result[9] = scratchThirdCartesian.y;
        result[10] = scratchThirdCartesian.z;
        result[11] = 0.0;
        result[12] = origin.x;
        result[13] = origin.y;
        result[14] = origin.z;
        result[15] = 1.0;
        return result;
      };
      localFrameToFixedFrameCache[hashAxis] = resultat;
    }
    return resultat;
  };
  
  /**
   * Computes a 4x4 transformation matrix from a reference frame with an east-north-up axes
   * centered at the provided origin to the provided ellipsoid's fixed reference frame.
   * The local axes are defined as:
   * <ul>
   * <li>The <code>x</code> axis points in the local east direction.</li>
   * <li>The <code>y</code> axis points in the local north direction.</li>
   * <li>The <code>z</code> axis points in the direction of the ellipsoid surface normal which passes through the position.</li>
   * </ul>
   *
   * @function
   * @param {Cartesian3} origin The center point of the local reference frame.
   * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation.
   * @param {Matrix4} [result] The object onto which to store the result.
   * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided.
   *
   * @example
   * // Get the transform from local east-north-up at cartographic (0.0, 0.0) to Earth's fixed frame.
   * var center = Cesium.Cartesian3.fromDegrees(0.0, 0.0);
   * var transform = Cesium.Transforms.eastNorthUpToFixedFrame(center);
   */
  Transforms.eastNorthUpToFixedFrame = Transforms.localFrameToFixedFrameGenerator(
    "east",
    "north"
  );
  
  /**
   * Computes a 4x4 transformation matrix from a reference frame with an north-east-down axes
   * centered at the provided origin to the provided ellipsoid's fixed reference frame.
   * The local axes are defined as:
   * <ul>
   * <li>The <code>x</code> axis points in the local north direction.</li>
   * <li>The <code>y</code> axis points in the local east direction.</li>
   * <li>The <code>z</code> axis points in the opposite direction of the ellipsoid surface normal which passes through the position.</li>
   * </ul>
   *
   * @function
   * @param {Cartesian3} origin The center point of the local reference frame.
   * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation.
   * @param {Matrix4} [result] The object onto which to store the result.
   * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided.
   *
   * @example
   * // Get the transform from local north-east-down at cartographic (0.0, 0.0) to Earth's fixed frame.
   * var center = Cesium.Cartesian3.fromDegrees(0.0, 0.0);
   * var transform = Cesium.Transforms.northEastDownToFixedFrame(center);
   */
  Transforms.northEastDownToFixedFrame = Transforms.localFrameToFixedFrameGenerator(
    "north",
    "east"
  );
  
  /**
   * Computes a 4x4 transformation matrix from a reference frame with an north-up-east axes
   * centered at the provided origin to the provided ellipsoid's fixed reference frame.
   * The local axes are defined as:
   * <ul>
   * <li>The <code>x</code> axis points in the local north direction.</li>
   * <li>The <code>y</code> axis points in the direction of the ellipsoid surface normal which passes through the position.</li>
   * <li>The <code>z</code> axis points in the local east direction.</li>
   * </ul>
   *
   * @function
   * @param {Cartesian3} origin The center point of the local reference frame.
   * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation.
   * @param {Matrix4} [result] The object onto which to store the result.
   * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided.
   *
   * @example
   * // Get the transform from local north-up-east at cartographic (0.0, 0.0) to Earth's fixed frame.
   * var center = Cesium.Cartesian3.fromDegrees(0.0, 0.0);
   * var transform = Cesium.Transforms.northUpEastToFixedFrame(center);
   */
  Transforms.northUpEastToFixedFrame = Transforms.localFrameToFixedFrameGenerator(
    "north",
    "up"
  );
  
  /**
   * Computes a 4x4 transformation matrix from a reference frame with an north-west-up axes
   * centered at the provided origin to the provided ellipsoid's fixed reference frame.
   * The local axes are defined as:
   * <ul>
   * <li>The <code>x</code> axis points in the local north direction.</li>
   * <li>The <code>y</code> axis points in the local west direction.</li>
   * <li>The <code>z</code> axis points in the direction of the ellipsoid surface normal which passes through the position.</li>
   * </ul>
   *
   * @function
   * @param {Cartesian3} origin The center point of the local reference frame.
   * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation.
   * @param {Matrix4} [result] The object onto which to store the result.
   * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided.
   *
   * @example
   * // Get the transform from local north-West-Up at cartographic (0.0, 0.0) to Earth's fixed frame.
   * var center = Cesium.Cartesian3.fromDegrees(0.0, 0.0);
   * var transform = Cesium.Transforms.northWestUpToFixedFrame(center);
   */
  Transforms.northWestUpToFixedFrame = Transforms.localFrameToFixedFrameGenerator(
    "north",
    "west"
  );
  
  var scratchHPRQuaternion = new Quaternion();
  var scratchScale = new Cartesian3(1.0, 1.0, 1.0);
  var scratchHPRMatrix4 = new Matrix4();
  
  /**
   * Computes a 4x4 transformation matrix from a reference frame with axes computed from the heading-pitch-roll angles
   * centered at the provided origin to the provided ellipsoid's fixed reference frame. Heading is the rotation from the local north
   * direction where a positive angle is increasing eastward. Pitch is the rotation from the local east-north plane. Positive pitch angles
   * are above the plane. Negative pitch angles are below the plane. Roll is the first rotation applied about the local east axis.
   *
   * @param {Cartesian3} origin The center point of the local reference frame.
   * @param {HeadingPitchRoll} headingPitchRoll The heading, pitch, and roll.
   * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation.
   * @param {Transforms.LocalFrameToFixedFrame} [fixedFrameTransform=Transforms.eastNorthUpToFixedFrame] A 4x4 transformation
   *  matrix from a reference frame to the provided ellipsoid's fixed reference frame
   * @param {Matrix4} [result] The object onto which to store the result.
   * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if none was provided.
   *
   * @example
   * // Get the transform from local heading-pitch-roll at cartographic (0.0, 0.0) to Earth's fixed frame.
   * var center = Cesium.Cartesian3.fromDegrees(0.0, 0.0);
   * var heading = -Cesium.Math.PI_OVER_TWO;
   * var pitch = Cesium.Math.PI_OVER_FOUR;
   * var roll = 0.0;
   * var hpr = new Cesium.HeadingPitchRoll(heading, pitch, roll);
   * var transform = Cesium.Transforms.headingPitchRollToFixedFrame(center, hpr);
   */
  Transforms.headingPitchRollToFixedFrame = function (
    origin,
    headingPitchRoll,
    ellipsoid,
    fixedFrameTransform,
    result
  ) {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object("HeadingPitchRoll", headingPitchRoll);
    //>>includeEnd('debug');
  
    fixedFrameTransform = defaultValue(
      fixedFrameTransform,
      Transforms.eastNorthUpToFixedFrame
    );
    var hprQuaternion = Quaternion.fromHeadingPitchRoll(
      headingPitchRoll,
      scratchHPRQuaternion
    );
    var hprMatrix = Matrix4.fromTranslationQuaternionRotationScale(
      Cartesian3.ZERO,
      hprQuaternion,
      scratchScale,
      scratchHPRMatrix4
    );
    result = fixedFrameTransform(origin, ellipsoid, result);
    return Matrix4.multiply(result, hprMatrix, result);
  };
  
  var scratchENUMatrix4 = new Matrix4();
  var scratchHPRMatrix3 = new Matrix3();
  
  /**
   * Computes a quaternion from a reference frame with axes computed from the heading-pitch-roll angles
   * centered at the provided origin. Heading is the rotation from the local north
   * direction where a positive angle is increasing eastward. Pitch is the rotation from the local east-north plane. Positive pitch angles
   * are above the plane. Negative pitch angles are below the plane. Roll is the first rotation applied about the local east axis.
   *
   * @param {Cartesian3} origin The center point of the local reference frame.
   * @param {HeadingPitchRoll} headingPitchRoll The heading, pitch, and roll.
   * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation.
   * @param {Transforms.LocalFrameToFixedFrame} [fixedFrameTransform=Transforms.eastNorthUpToFixedFrame] A 4x4 transformation
   *  matrix from a reference frame to the provided ellipsoid's fixed reference frame
   * @param {Quaternion} [result] The object onto which to store the result.
   * @returns {Quaternion} The modified result parameter or a new Quaternion instance if none was provided.
   *
   * @example
   * // Get the quaternion from local heading-pitch-roll at cartographic (0.0, 0.0) to Earth's fixed frame.
   * var center = Cesium.Cartesian3.fromDegrees(0.0, 0.0);
   * var heading = -Cesium.Math.PI_OVER_TWO;
   * var pitch = Cesium.Math.PI_OVER_FOUR;
   * var roll = 0.0;
   * var hpr = new HeadingPitchRoll(heading, pitch, roll);
   * var quaternion = Cesium.Transforms.headingPitchRollQuaternion(center, hpr);
   */
  Transforms.headingPitchRollQuaternion = function (
    origin,
    headingPitchRoll,
    ellipsoid,
    fixedFrameTransform,
    result
  ) {
    //>>includeStart('debug', pragmas.debug);
    Check.typeOf.object("HeadingPitchRoll", headingPitchRoll);
    //>>includeEnd('debug');
  
    var transform = Transforms.headingPitchRollToFixedFrame(
      origin,
      headingPitchRoll,
      ellipsoid,
      fixedFrameTransform,
      scratchENUMatrix4
    );
    var rotation = Matrix4.getMatrix3(transform, scratchHPRMatrix3);
    return Quaternion.fromRotationMatrix(rotation, result);
  };
  
  var noScale = new Cartesian3(1.0, 1.0, 1.0);
  var hprCenterScratch = new Cartesian3();
  var ffScratch = new Matrix4();
  var hprTransformScratch = new Matrix4();
  var hprRotationScratch = new Matrix3();
  var hprQuaternionScratch = new Quaternion();
  /**
   * Computes heading-pitch-roll angles from a transform in a particular reference frame. Heading is the rotation from the local north
   * direction where a positive angle is increasing eastward. Pitch is the rotation from the local east-north plane. Positive pitch angles
   * are above the plane. Negative pitch angles are below the plane. Roll is the first rotation applied about the local east axis.
   *
   * @param {Matrix4} transform The transform
   * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation.
   * @param {Transforms.LocalFrameToFixedFrame} [fixedFrameTransform=Transforms.eastNorthUpToFixedFrame] A 4x4 transformation
   *  matrix from a reference frame to the provided ellipsoid's fixed reference frame
   * @param {HeadingPitchRoll} [result] The object onto which to store the result.
   * @returns {HeadingPitchRoll} The modified result parameter or a new HeadingPitchRoll instance if none was provided.
   */
  Transforms.fixedFrameToHeadingPitchRoll = function (
    transform,
    ellipsoid,
    fixedFrameTransform,
    result
  ) {
    //>>includeStart('debug', pragmas.debug);
    Check.defined("transform", transform);
    //>>includeEnd('debug');
  
    ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84);
    fixedFrameTransform = defaultValue(
      fixedFrameTransform,
      Transforms.eastNorthUpToFixedFrame
    );
    if (!defined(result)) {
      result = new HeadingPitchRoll();
    }
  
    var center = Matrix4.getTranslation(transform, hprCenterScratch);
    if (Cartesian3.equals(center, Cartesian3.ZERO)) {
      result.heading = 0;
      result.pitch = 0;
      result.roll = 0;
      return result;
    }
    var toFixedFrame = Matrix4.inverseTransformation(
      fixedFrameTransform(center, ellipsoid, ffScratch),
      ffScratch
    );
    var transformCopy = Matrix4.setScale(transform, noScale, hprTransformScratch);
    transformCopy = Matrix4.setTranslation(
      transformCopy,
      Cartesian3.ZERO,
      transformCopy
    );
  
    toFixedFrame = Matrix4.multiply(toFixedFrame, transformCopy, toFixedFrame);
    var quaternionRotation = Quaternion.fromRotationMatrix(
      Matrix4.getMatrix3(toFixedFrame, hprRotationScratch),
      hprQuaternionScratch
    );
    quaternionRotation = Quaternion.normalize(
      quaternionRotation,
      quaternionRotation
    );
  
    return HeadingPitchRoll.fromQuaternion(quaternionRotation, result);
  };
  
  var gmstConstant0 = 6 * 3600 + 41 * 60 + 50.54841;
  var gmstConstant1 = 8640184.812866;
  var gmstConstant2 = 0.093104;
  var gmstConstant3 = -6.2e-6;
  var rateCoef = 1.1772758384668e-19;
  var wgs84WRPrecessing = 7.2921158553e-5;
  var twoPiOverSecondsInDay = CesiumMath.TWO_PI / 86400.0;
  var dateInUtc = new JulianDate();
  
  /**
   * Computes a rotation matrix to transform a point or vector from True Equator Mean Equinox (TEME) axes to the
   * pseudo-fixed axes at a given time.  This method treats the UT1 time standard as equivalent to UTC.
   *
   * @param {JulianDate} date The time at which to compute the rotation matrix.
   * @param {Matrix3} [result] The object onto which to store the result.
   * @returns {Matrix3} The modified result parameter or a new Matrix3 instance if none was provided.
   *
   * @example
   * //Set the view to the inertial frame.
   * scene.postUpdate.addEventListener(function(scene, time) {
   *    var now = Cesium.JulianDate.now();
   *    var offset = Cesium.Matrix4.multiplyByPoint(camera.transform, camera.position, new Cesium.Cartesian3());
   *    var transform = Cesium.Matrix4.fromRotationTranslation(Cesium.Transforms.computeTemeToPseudoFixedMatrix(now));
   *    var inverseTransform = Cesium.Matrix4.inverseTransformation(transform, new Cesium.Matrix4());
   *    Cesium.Matrix4.multiplyByPoint(inverseTransform, offset, offset);
   *    camera.lookAtTransform(transform, offset);
   * });
   */
  Transforms.computeTemeToPseudoFixedMatrix = function (date, result) {
    //>>includeStart('debug', pragmas.debug);
    if (!defined(date)) {
      throw new DeveloperError("date is required.");
    }
    //>>includeEnd('debug');
  
    // GMST is actually computed using UT1.  We're using UTC as an approximation of UT1.
    // We do not want to use the function like convertTaiToUtc in JulianDate because
    // we explicitly do not want to fail when inside the leap second.
  
    dateInUtc = JulianDate.addSeconds(
      date,
      -JulianDate.computeTaiMinusUtc(date),
      dateInUtc
    );
    var utcDayNumber = dateInUtc.dayNumber;
    var utcSecondsIntoDay = dateInUtc.secondsOfDay;
  
    var t;
    var diffDays = utcDayNumber - 2451545;
    if (utcSecondsIntoDay >= 43200.0) {
      t = (diffDays + 0.5) / TimeConstants.DAYS_PER_JULIAN_CENTURY;
    } else {
      t = (diffDays - 0.5) / TimeConstants.DAYS_PER_JULIAN_CENTURY;
    }
  
    var gmst0 =
      gmstConstant0 +
      t * (gmstConstant1 + t * (gmstConstant2 + t * gmstConstant3));
    var angle = (gmst0 * twoPiOverSecondsInDay) % CesiumMath.TWO_PI;
    var ratio = wgs84WRPrecessing + rateCoef * (utcDayNumber - 2451545.5);
    var secondsSinceMidnight =
      (utcSecondsIntoDay + TimeConstants.SECONDS_PER_DAY * 0.5) %
      TimeConstants.SECONDS_PER_DAY;
    var gha = angle + ratio * secondsSinceMidnight;
    var cosGha = Math.cos(gha);
    var sinGha = Math.sin(gha);
  
    if (!defined(result)) {
      return new Matrix3(
        cosGha,
        sinGha,
        0.0,
        -sinGha,
        cosGha,
        0.0,
        0.0,
        0.0,
        1.0
      );
    }
    result[0] = cosGha;
    result[1] = -sinGha;
    result[2] = 0.0;
    result[3] = sinGha;
    result[4] = cosGha;
    result[5] = 0.0;
    result[6] = 0.0;
    result[7] = 0.0;
    result[8] = 1.0;
    return result;
  };
  
  /**
   * The source of IAU 2006 XYS data, used for computing the transformation between the
   * Fixed and ICRF axes.
   * @type {Iau2006XysData}
   *
   * @see Transforms.computeIcrfToFixedMatrix
   * @see Transforms.computeFixedToIcrfMatrix
   *
   * @private
   */
  Transforms.iau2006XysData = new Iau2006XysData();
  
  /**
   * The source of Earth Orientation Parameters (EOP) data, used for computing the transformation
   * between the Fixed and ICRF axes.  By default, zero values are used for all EOP values,
   * yielding a reasonable but not completely accurate representation of the ICRF axes.
   * @type {EarthOrientationParameters}
   *
   * @see Transforms.computeIcrfToFixedMatrix
   * @see Transforms.computeFixedToIcrfMatrix
   *
   * @private
   */
  Transforms.earthOrientationParameters = EarthOrientationParameters.NONE;
  
  var ttMinusTai = 32.184;
  var j2000ttDays = 2451545.0;
  
  /**
   * Preloads the data necessary to transform between the ICRF and Fixed axes, in either
   * direction, over a given interval.  This function returns a promise that, when resolved,
   * indicates that the preload has completed.
   *
   * @param {TimeInterval} timeInterval The interval to preload.
   * @returns {Promise<void>} A promise that, when resolved, indicates that the preload has completed
   *          and evaluation of the transformation between the fixed and ICRF axes will
   *          no longer return undefined for a time inside the interval.
   *
   *
   * @example
   * var interval = new Cesium.TimeInterval(...);
   * when(Cesium.Transforms.preloadIcrfFixed(interval), function() {
   *     // the data is now loaded
   * });
   *
   * @see Transforms.computeIcrfToFixedMatrix
   * @see Transforms.computeFixedToIcrfMatrix
   * @see when
   */
  Transforms.preloadIcrfFixed = function (timeInterval) {
    var startDayTT = timeInterval.start.dayNumber;
    var startSecondTT = timeInterval.start.secondsOfDay + ttMinusTai;
    var stopDayTT = timeInterval.stop.dayNumber;
    var stopSecondTT = timeInterval.stop.secondsOfDay + ttMinusTai;
  
    var xysPromise = Transforms.iau2006XysData.preload(
      startDayTT,
      startSecondTT,
      stopDayTT,
      stopSecondTT
    );
    var eopPromise = Transforms.earthOrientationParameters.getPromiseToLoad();
  
    return when.all([xysPromise, eopPromise]);
  };
  
  /**
   * Computes a rotation matrix to transform a point or vector from the International Celestial
   * Reference Frame (GCRF/ICRF) inertial frame axes to the Earth-Fixed frame axes (ITRF)
   * at a given time.  This function may return undefined if the data necessary to
   * do the transformation is not yet loaded.
   *
   * @param {JulianDate} date The time at which to compute the rotation matrix.
   * @param {Matrix3} [result] The object onto which to store the result.  If this parameter is
   *                  not specified, a new instance is created and returned.
   * @returns {Matrix3} The rotation matrix, or undefined if the data necessary to do the
   *                   transformation is not yet loaded.
   *
   *
   * @example
   * scene.postUpdate.addEventListener(function(scene, time) {
   *   // View in ICRF.
   *   var icrfToFixed = Cesium.Transforms.computeIcrfToFixedMatrix(time);
   *   if (Cesium.defined(icrfToFixed)) {
   *     var offset = Cesium.Cartesian3.clone(camera.position);
   *     var transform = Cesium.Matrix4.fromRotationTranslation(icrfToFixed);
   *     camera.lookAtTransform(transform, offset);
   *   }
   * });
   *
   * @see Transforms.preloadIcrfFixed
   */
  Transforms.computeIcrfToFixedMatrix = function (date, result) {
    //>>includeStart('debug', pragmas.debug);
    if (!defined(date)) {
      throw new DeveloperError("date is required.");
    }
    //>>includeEnd('debug');
    if (!defined(result)) {
      result = new Matrix3();
    }
  
    var fixedToIcrfMtx = Transforms.computeFixedToIcrfMatrix(date, result);
    if (!defined(fixedToIcrfMtx)) {
      return undefined;
    }
  
    return Matrix3.transpose(fixedToIcrfMtx, result);
  };
  
  var xysScratch = new Iau2006XysSample(0.0, 0.0, 0.0);
  var eopScratch = new EarthOrientationParametersSample(
    0.0,
    0.0,
    0.0,
    0.0,
    0.0,
    0.0
  );
  var rotation1Scratch = new Matrix3();
  var rotation2Scratch = new Matrix3();
  
  /**
   * Computes a rotation matrix to transform a point or vector from the Earth-Fixed frame axes (ITRF)
   * to the International Celestial Reference Frame (GCRF/ICRF) inertial frame axes
   * at a given time.  This function may return undefined if the data necessary to
   * do the transformation is not yet loaded.
   *
   * @param {JulianDate} date The time at which to compute the rotation matrix.
   * @param {Matrix3} [result] The object onto which to store the result.  If this parameter is
   *                  not specified, a new instance is created and returned.
   * @returns {Matrix3} The rotation matrix, or undefined if the data necessary to do the
   *                   transformation is not yet loaded.
   *
   *
   * @example
   * // Transform a point from the ICRF axes to the Fixed axes.
   * var now = Cesium.JulianDate.now();
   * var pointInFixed = Cesium.Cartesian3.fromDegrees(0.0, 0.0);
   * var fixedToIcrf = Cesium.Transforms.computeIcrfToFixedMatrix(now);
   * var pointInInertial = new Cesium.Cartesian3();
   * if (Cesium.defined(fixedToIcrf)) {
   *     pointInInertial = Cesium.Matrix3.multiplyByVector(fixedToIcrf, pointInFixed, pointInInertial);
   * }
   *
   * @see Transforms.preloadIcrfFixed
   */
  Transforms.computeFixedToIcrfMatrix = function (date, result) {
    //>>includeStart('debug', pragmas.debug);
    if (!defined(date)) {
      throw new DeveloperError("date is required.");
    }
    //>>includeEnd('debug');
  
    if (!defined(result)) {
      result = new Matrix3();
    }
  
    // Compute pole wander
    var eop = Transforms.earthOrientationParameters.compute(date, eopScratch);
    if (!defined(eop)) {
      return undefined;
    }
  
    // There is no external conversion to Terrestrial Time (TT).
    // So use International Atomic Time (TAI) and convert using offsets.
    // Here we are assuming that dayTT and secondTT are positive
    var dayTT = date.dayNumber;
    // It's possible here that secondTT could roll over 86400
    // This does not seem to affect the precision (unit tests check for this)
    var secondTT = date.secondsOfDay + ttMinusTai;
  
    var xys = Transforms.iau2006XysData.computeXysRadians(
      dayTT,
      secondTT,
      xysScratch
    );
    if (!defined(xys)) {
      return undefined;
    }
  
    var x = xys.x + eop.xPoleOffset;
    var y = xys.y + eop.yPoleOffset;
  
    // Compute XYS rotation
    var a = 1.0 / (1.0 + Math.sqrt(1.0 - x * x - y * y));
  
    var rotation1 = rotation1Scratch;
    rotation1[0] = 1.0 - a * x * x;
    rotation1[3] = -a * x * y;
    rotation1[6] = x;
    rotation1[1] = -a * x * y;
    rotation1[4] = 1 - a * y * y;
    rotation1[7] = y;
    rotation1[2] = -x;
    rotation1[5] = -y;
    rotation1[8] = 1 - a * (x * x + y * y);
  
    var rotation2 = Matrix3.fromRotationZ(-xys.s, rotation2Scratch);
    var matrixQ = Matrix3.multiply(rotation1, rotation2, rotation1Scratch);
  
    // Similar to TT conversions above
    // It's possible here that secondTT could roll over 86400
    // This does not seem to affect the precision (unit tests check for this)
    var dateUt1day = date.dayNumber;
    var dateUt1sec =
      date.secondsOfDay - JulianDate.computeTaiMinusUtc(date) + eop.ut1MinusUtc;
  
    // Compute Earth rotation angle
    // The IERS standard for era is
    //    era = 0.7790572732640 + 1.00273781191135448 * Tu
    // where
    //    Tu = JulianDateInUt1 - 2451545.0
    // However, you get much more precision if you make the following simplification
    //    era = a + (1 + b) * (JulianDayNumber + FractionOfDay - 2451545)
    //    era = a + (JulianDayNumber - 2451545) + FractionOfDay + b (JulianDayNumber - 2451545 + FractionOfDay)
    //    era = a + FractionOfDay + b (JulianDayNumber - 2451545 + FractionOfDay)
    // since (JulianDayNumber - 2451545) represents an integer number of revolutions which will be discarded anyway.
    var daysSinceJ2000 = dateUt1day - 2451545;
    var fractionOfDay = dateUt1sec / TimeConstants.SECONDS_PER_DAY;
    var era =
      0.779057273264 +
      fractionOfDay +
      0.00273781191135448 * (daysSinceJ2000 + fractionOfDay);
    era = (era % 1.0) * CesiumMath.TWO_PI;
  
    var earthRotation = Matrix3.fromRotationZ(era, rotation2Scratch);
  
    // pseudoFixed to ICRF
    var pfToIcrf = Matrix3.multiply(matrixQ, earthRotation, rotation1Scratch);
  
    // Compute pole wander matrix
    var cosxp = Math.cos(eop.xPoleWander);
    var cosyp = Math.cos(eop.yPoleWander);
    var sinxp = Math.sin(eop.xPoleWander);
    var sinyp = Math.sin(eop.yPoleWander);
  
    var ttt = dayTT - j2000ttDays + secondTT / TimeConstants.SECONDS_PER_DAY;
    ttt /= 36525.0;
  
    // approximate sp value in rad
    var sp = (-47.0e-6 * ttt * CesiumMath.RADIANS_PER_DEGREE) / 3600.0;
    var cossp = Math.cos(sp);
    var sinsp = Math.sin(sp);
  
    var fToPfMtx = rotation2Scratch;
    fToPfMtx[0] = cosxp * cossp;
    fToPfMtx[1] = cosxp * sinsp;
    fToPfMtx[2] = sinxp;
    fToPfMtx[3] = -cosyp * sinsp + sinyp * sinxp * cossp;
    fToPfMtx[4] = cosyp * cossp + sinyp * sinxp * sinsp;
    fToPfMtx[5] = -sinyp * cosxp;
    fToPfMtx[6] = -sinyp * sinsp - cosyp * sinxp * cossp;
    fToPfMtx[7] = sinyp * cossp - cosyp * sinxp * sinsp;
    fToPfMtx[8] = cosyp * cosxp;
  
    return Matrix3.multiply(pfToIcrf, fToPfMtx, result);
  };
  
  var pointToWindowCoordinatesTemp = new Cartesian4();
  
  /**
   * Transform a point from model coordinates to window coordinates.
   *
   * @param {Matrix4} modelViewProjectionMatrix The 4x4 model-view-projection matrix.
   * @param {Matrix4} viewportTransformation The 4x4 viewport transformation.
   * @param {Cartesian3} point The point to transform.
   * @param {Cartesian2} [result] The object onto which to store the result.
   * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if none was provided.
   */
  Transforms.pointToWindowCoordinates = function (
    modelViewProjectionMatrix,
    viewportTransformation,
    point,
    result
  ) {
    result = Transforms.pointToGLWindowCoordinates(
      modelViewProjectionMatrix,
      viewportTransformation,
      point,
      result
    );
    result.y = 2.0 * viewportTransformation[5] - result.y;
    return result;
  };
  
  /**
   * @private
   */
  Transforms.pointToGLWindowCoordinates = function (
    modelViewProjectionMatrix,
    viewportTransformation,
    point,
    result
  ) {
    //>>includeStart('debug', pragmas.debug);
    if (!defined(modelViewProjectionMatrix)) {
      throw new DeveloperError("modelViewProjectionMatrix is required.");
    }
  
    if (!defined(viewportTransformation)) {
      throw new DeveloperError("viewportTransformation is required.");
    }
  
    if (!defined(point)) {
      throw new DeveloperError("point is required.");
    }
    //>>includeEnd('debug');
  
    if (!defined(result)) {
      result = new Cartesian2();
    }
  
    var tmp = pointToWindowCoordinatesTemp;
  
    Matrix4.multiplyByVector(
      modelViewProjectionMatrix,
      Cartesian4.fromElements(point.x, point.y, point.z, 1, tmp),
      tmp
    );
    Cartesian4.multiplyByScalar(tmp, 1.0 / tmp.w, tmp);
    Matrix4.multiplyByVector(viewportTransformation, tmp, tmp);
    return Cartesian2.fromCartesian4(tmp, result);
  };
  
  var normalScratch = new Cartesian3();
  var rightScratch = new Cartesian3();
  var upScratch = new Cartesian3();
  
  /**
   * Transform a position and velocity to a rotation matrix.
   *
   * @param {Cartesian3} position The position to transform.
   * @param {Cartesian3} velocity The velocity vector to transform.
   * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation.
   * @param {Matrix3} [result] The object onto which to store the result.
   * @returns {Matrix3} The modified result parameter or a new Matrix3 instance if none was provided.
   */
  Transforms.rotationMatrixFromPositionVelocity = function (
    position,
    velocity,
    ellipsoid,
    result
  ) {
    //>>includeStart('debug', pragmas.debug);
    if (!defined(position)) {
      throw new DeveloperError("position is required.");
    }
  
    if (!defined(velocity)) {
      throw new DeveloperError("velocity is required.");
    }
    //>>includeEnd('debug');
  
    var normal = defaultValue(ellipsoid, Ellipsoid.WGS84).geodeticSurfaceNormal(
      position,
      normalScratch
    );
    var right = Cartesian3.cross(velocity, normal, rightScratch);
  
    if (Cartesian3.equalsEpsilon(right, Cartesian3.ZERO, CesiumMath.EPSILON6)) {
      right = Cartesian3.clone(Cartesian3.UNIT_X, right);
    }
  
    var up = Cartesian3.cross(right, velocity, upScratch);
    Cartesian3.normalize(up, up);
    Cartesian3.cross(velocity, up, right);
    Cartesian3.negate(right, right);
    Cartesian3.normalize(right, right);
  
    if (!defined(result)) {
      result = new Matrix3();
    }
  
    result[0] = velocity.x;
    result[1] = velocity.y;
    result[2] = velocity.z;
    result[3] = right.x;
    result[4] = right.y;
    result[5] = right.z;
    result[6] = up.x;
    result[7] = up.y;
    result[8] = up.z;
  
    return result;
  };
  
  var swizzleMatrix = new Matrix4(
    0.0,
    0.0,
    1.0,
    0.0,
    1.0,
    0.0,
    0.0,
    0.0,
    0.0,
    1.0,
    0.0,
    0.0,
    0.0,
    0.0,
    0.0,
    1.0
  );
  
  var scratchCartographic = new Cartographic();
  var scratchCartesian3Projection = new Cartesian3();
  var scratchCenter = new Cartesian3();
  var scratchRotation = new Matrix3();
  var scratchFromENU = new Matrix4();
  var scratchToENU = new Matrix4();
  
  /**
   * @private
   */
  Transforms.basisTo2D = function (projection, matrix, result) {
    //>>includeStart('debug', pragmas.debug);
    if (!defined(projection)) {
      throw new DeveloperError("projection is required.");
    }
    if (!defined(matrix)) {
      throw new DeveloperError("matrix is required.");
    }
    if (!defined(result)) {
      throw new DeveloperError("result is required.");
    }
    //>>includeEnd('debug');
  
    var rtcCenter = Matrix4.getTranslation(matrix, scratchCenter);
    var ellipsoid = projection.ellipsoid;
  
    // Get the 2D Center
    var cartographic = ellipsoid.cartesianToCartographic(
      rtcCenter,
      scratchCartographic
    );
    var projectedPosition = projection.project(
      cartographic,
      scratchCartesian3Projection
    );
    Cartesian3.fromElements(
      projectedPosition.z,
      projectedPosition.x,
      projectedPosition.y,
      projectedPosition
    );
  
    // Assuming the instance are positioned in WGS84, invert the WGS84 transform to get the local transform and then convert to 2D
    var fromENU = Transforms.eastNorthUpToFixedFrame(
      rtcCenter,
      ellipsoid,
      scratchFromENU
    );
    var toENU = Matrix4.inverseTransformation(fromENU, scratchToENU);
    var rotation = Matrix4.getMatrix3(matrix, scratchRotation);
    var local = Matrix4.multiplyByMatrix3(toENU, rotation, result);
    Matrix4.multiply(swizzleMatrix, local, result); // Swap x, y, z for 2D
    Matrix4.setTranslation(result, projectedPosition, result); // Use the projected center
  
    return result;
  };
  
  /**
   * @private
   */
  Transforms.wgs84To2DModelMatrix = function (projection, center, result) {
    //>>includeStart('debug', pragmas.debug);
    if (!defined(projection)) {
      throw new DeveloperError("projection is required.");
    }
    if (!defined(center)) {
      throw new DeveloperError("center is required.");
    }
    if (!defined(result)) {
      throw new DeveloperError("result is required.");
    }
    //>>includeEnd('debug');
  
    var ellipsoid = projection.ellipsoid;
  
    var fromENU = Transforms.eastNorthUpToFixedFrame(
      center,
      ellipsoid,
      scratchFromENU
    );
    var toENU = Matrix4.inverseTransformation(fromENU, scratchToENU);
  
    var cartographic = ellipsoid.cartesianToCartographic(
      center,
      scratchCartographic
    );
    var projectedPosition = projection.project(
      cartographic,
      scratchCartesian3Projection
    );
    Cartesian3.fromElements(
      projectedPosition.z,
      projectedPosition.x,
      projectedPosition.y,
      projectedPosition
    );
  
    var translation = Matrix4.fromTranslation(projectedPosition, scratchFromENU);
    Matrix4.multiply(swizzleMatrix, toENU, result);
    Matrix4.multiply(translation, result, result);
  
    return result;
  };
  export default Transforms;
  