diff --git a/Apps/Sandcastle/gallery/development/HeadingPitchRoll.html b/Apps/Sandcastle/gallery/development/HeadingPitchRoll.html new file mode 100644 index 000000000000..f210e3561b8d --- /dev/null +++ b/Apps/Sandcastle/gallery/development/HeadingPitchRoll.html @@ -0,0 +1,237 @@ + + + + + + + + + + Cesium Demo + + + + + + + +
+
+

Loading...

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Heading: °
← to left/→ to right
Pitch: °
↑ to up/↓ to down
roll: °
← + ⇧ left/→ + ⇧ right
Speed: m/s
↑ + ⇧ to speed up/↓ + ⇧ to speed down
following aircraft + +
+
+ + + + \ No newline at end of file diff --git a/Apps/Sandcastle/gallery/development/HeadingPitchRoll.jpg b/Apps/Sandcastle/gallery/development/HeadingPitchRoll.jpg new file mode 100644 index 000000000000..b06301c8a0a4 Binary files /dev/null and b/Apps/Sandcastle/gallery/development/HeadingPitchRoll.jpg differ diff --git a/CHANGES.md b/CHANGES.md index 5dda401ff79f..8b131600f7a8 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -20,6 +20,15 @@ Change Log * Added the ability to specify retina options, such as `@2x.png`, via the `MapboxImageryProvider` `format` option. * Removed an unnecessary reprojection of Web Mercator imagery tiles to the Geographic projection on load. This should improve both visual quality and load performance slightly. * Fix a issue where a billboard entity would not render after toggling the show propery. [#4408](https://github.com/AnalyticalGraphicsInc/cesium/issues/4408) +* Added `Transforms.northUpEastToFixedFrame` to compute a 4x4 local transformation matrix from a reference frame with an north-west-up axes. +* Added `Transforms.aircraftHeadingPitchRollToFixedFrame` to create a local frame from a position and heading/pitch/roll angles. The local frame is north-west-up axed. +* Added `Transforms.aircraftHeadingPitchRollQuaternion` which is the quaternion rotation from `Transforms.aircraftHeadingPitchRollToFixedFrame`. +* Added `HeadingPitchRoll` : + * `HeadingPitchRoll.fromQuaternion` function for retrieving heading-pitch-roll angles from a quaternion. + * `HeadingPitchRoll.fromDegrees` function that returns a new HeadingPitchRoll instance from angles given in degrees. + * `HeadingPitchRoll.clone` function to duplicate HeadingPitchRoll instance. + * `HeadingPitchRoll.equals` and `HeadingPitchRoll.equalsEpsilon` functions for comparing two instances. +* Added `Matrix3.fromHeadingPitchRoll` Computes a 3x3 rotation matrix from the provided headingPitchRoll. ### 1.26 - 2016-10-03 diff --git a/Source/Core/HeadingPitchRoll.js b/Source/Core/HeadingPitchRoll.js new file mode 100644 index 000000000000..32229a9880f7 --- /dev/null +++ b/Source/Core/HeadingPitchRoll.js @@ -0,0 +1,190 @@ +/*global define*/ +define([ + './defaultValue', + './defined', + './DeveloperError', + './Math' +], function( + defaultValue, + defined, + DeveloperError, + CesiumMath) { + "use strict"; + + /** + * A rotation expressed as a heading, pitch, and roll. Heading is the rotation about the + * negative z axis. Pitch is the rotation about the negative y axis. Roll is the rotation about + * the positive x axis. + * @alias HeadingPitchRoll + * @constructor + * + * @param {Number} [heading=0.0] The heading component in radians. + * @param {Number} [pitch=0.0] The pitch component in radians. + * @param {Number} [roll=0.0] The roll component in radians. + */ + function HeadingPitchRoll(heading, pitch, roll) { + this.heading = defaultValue(heading, 0.0); + this.pitch = defaultValue(pitch, 0.0); + this.roll = defaultValue(roll, 0.0); + } + + /** + * Computes the heading, pitch and roll from a quaternion (see http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles ) + * + * @param {Quaternion} quaternion The quaternion from which to retrieve heading, pitch, and roll, all expressed in radians. + * @param {Quaternion} [result] The object in which to store the result. If not provided, a new instance is created and returned. + * @returns {HeadingPitchRoll} The modified result parameter or a new HeadingPitchRoll instance if one was not provided. + */ + HeadingPitchRoll.fromQuaternion = function(quaternion, result) { + //>>includeStart('debug', pragmas.debug); + if (!defined(quaternion)) { + throw new DeveloperError('quaternion is required'); + } + //>>includeEnd('debug'); + if (!defined(result)) { + result = new HeadingPitchRoll(); + } + var test = 2 * (quaternion.w * quaternion.y - quaternion.z * quaternion.x); + var denominatorRoll = 1 - 2 * (quaternion.x * quaternion.x + quaternion.y * quaternion.y); + var numeratorRoll = 2 * (quaternion.w * quaternion.x + quaternion.y * quaternion.z); + var denominatorHeading = 1 - 2 * (quaternion.y * quaternion.y + quaternion.z * quaternion.z); + var numeratorHeading = 2 * (quaternion.w * quaternion.z + quaternion.x * quaternion.y); + result.heading = -Math.atan2(numeratorHeading, denominatorHeading); + result.roll = Math.atan2(numeratorRoll, denominatorRoll); + result.pitch = -Math.asin(test); + return result; + }; + + /** + * Returns a new HeadingPitchRoll instance from angles given in degrees. + * + * @param {Number} heading the heading in degrees + * @param {Number} pitch the pitch in degrees + * @param {Number} roll the heading in degrees + * @param {HeadingPitchRoll} [result] The object in which to store the result. If not provided, a new instance is created and returned. + * @returns {HeadingPitchRoll} A new HeadingPitchRoll instance + */ + HeadingPitchRoll.fromDegrees = function(heading, pitch, roll, result) { + //>>includeStart('debug', pragmas.debug); + if (!defined(heading)) { + throw new DeveloperError('heading is required'); + } + if (!defined(pitch)) { + throw new DeveloperError('pitch is required'); + } + if (!defined(roll)) { + throw new DeveloperError('roll is required'); + } + //>>includeEnd('debug'); + if (!defined(result)) { + result = new HeadingPitchRoll(); + } + result.heading = heading * CesiumMath.RADIANS_PER_DEGREE; + result.pitch = pitch * CesiumMath.RADIANS_PER_DEGREE; + result.roll = roll * CesiumMath.RADIANS_PER_DEGREE; + return result; + }; + + /** + * Duplicates a HeadingPitchRoll instance. + * + * @param {HeadingPitchRoll} headingPitchRoll The HeadingPitchRoll to duplicate. + * @param {HeadingPitchRoll} [result] The object onto which to store the result. + * @returns {HeadingPitchRoll} The modified result parameter or a new HeadingPitchRoll instance if one was not provided. (Returns undefined if headingPitchRoll is undefined) + */ + HeadingPitchRoll.clone = function(headingPitchRoll, result) { + if (!defined(headingPitchRoll)) { + return undefined; + } + if (!defined(result)) { + return new HeadingPitchRoll(headingPitchRoll.heading, headingPitchRoll.pitch, headingPitchRoll.roll); + } + result.heading = headingPitchRoll.heading; + result.pitch = headingPitchRoll.pitch; + result.roll = headingPitchRoll.roll; + return result; + }; + + /** + * Compares the provided HeadingPitchRolls componentwise and returns + * true if they are equal, false otherwise. + * + * @param {HeadingPitchRoll} [left] The first HeadingPitchRoll. + * @param {HeadingPitchRoll} [right] The second HeadingPitchRoll. + * @returns {Boolean} true if left and right are equal, false otherwise. + */ + HeadingPitchRoll.equals = function(left, right) { + return (left === right) || + ((defined(left)) && + (defined(right)) && + (left.heading === right.heading) && + (left.pitch === right.pitch) && + (left.roll === right.roll)); + }; + + /** + * Compares the provided HeadingPitchRolls componentwise and returns + * true if they pass an absolute or relative tolerance test, + * false otherwise. + * + * @param {HeadingPitchRoll} [left] The first HeadingPitchRoll. + * @param {HeadingPitchRoll} [right] The second HeadingPitchRoll. + * @param {Number} relativeEpsilon The relative epsilon tolerance to use for equality testing. + * @param {Number} [absoluteEpsilon=relativeEpsilon] The absolute epsilon tolerance to use for equality testing. + * @returns {Boolean} true if left and right are within the provided epsilon, false otherwise. + */ + HeadingPitchRoll.equalsEpsilon = function(left, right, relativeEpsilon, absoluteEpsilon) { + return (left === right) || + (defined(left) && + defined(right) && + CesiumMath.equalsEpsilon(left.heading, right.heading, relativeEpsilon, absoluteEpsilon) && + CesiumMath.equalsEpsilon(left.pitch, right.pitch, relativeEpsilon, absoluteEpsilon) && + CesiumMath.equalsEpsilon(left.roll, right.roll, relativeEpsilon, absoluteEpsilon)); + }; + + /** + * Duplicates this HeadingPitchRoll instance. + * + * @param {HeadingPitchRoll} [result] The object onto which to store the result. + * @returns {HeadingPitchRoll} The modified result parameter or a new HeadingPitchRoll instance if one was not provided. + */ + HeadingPitchRoll.prototype.clone = function(result) { + return HeadingPitchRoll.clone(this, result); + }; + + /** + * Compares this HeadingPitchRoll against the provided HeadingPitchRoll componentwise and returns + * true if they are equal, false otherwise. + * + * @param {HeadingPitchRoll} [right] The right hand side HeadingPitchRoll. + * @returns {Boolean} true if they are equal, false otherwise. + */ + HeadingPitchRoll.prototype.equals = function(right) { + return HeadingPitchRoll.equals(this, right); + }; + + /** + * Compares this HeadingPitchRoll against the provided HeadingPitchRoll componentwise and returns + * true if they pass an absolute or relative tolerance test, + * false otherwise. + * + * @param {HeadingPitchRoll} [right] The right hand side HeadingPitchRoll. + * @param {Number} relativeEpsilon The relative epsilon tolerance to use for equality testing. + * @param {Number} [absoluteEpsilon=relativeEpsilon] The absolute epsilon tolerance to use for equality testing. + * @returns {Boolean} true if they are within the provided epsilon, false otherwise. + */ + HeadingPitchRoll.prototype.equalsEpsilon = function(right, relativeEpsilon, absoluteEpsilon) { + return HeadingPitchRoll.equalsEpsilon(this, right, relativeEpsilon, absoluteEpsilon); + }; + + /** + * Creates a string representing this HeadingPitchRoll in the format '(heading, pitch, roll)' in radians. + * + * @returns {String} A string representing the provided HeadingPitchRoll in the format '(heading, pitch, roll)'. + */ + HeadingPitchRoll.prototype.toString = function() { + return '(' + this.heading + ', ' + this.pitch + ', ' + this.roll + ')'; + }; + + return HeadingPitchRoll; +}); diff --git a/Source/Core/Matrix3.js b/Source/Core/Matrix3.js index c6c3e26567cb..a483318f04cc 100644 --- a/Source/Core/Matrix3.js +++ b/Source/Core/Matrix3.js @@ -6,6 +6,7 @@ define([ './defineProperties', './DeveloperError', './freezeObject', + './HeadingPitchRoll', './Math' ], function( Cartesian3, @@ -14,6 +15,7 @@ define([ defineProperties, DeveloperError, freezeObject, + HeadingPitchRoll, CesiumMath) { 'use strict'; @@ -306,6 +308,55 @@ define([ return result; }; + /** + * Computes a 3x3 rotation matrix from the provided headingPitchRoll. (see http://en.wikipedia.org/wiki/Conversion_between_quaternions_and_Euler_angles ) + * + * @param {HeadingPitchRoll} headingPitchRoll the headingPitchRoll to use. + * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns {Matrix3} The 3x3 rotation matrix from this headingPitchRoll. + */ + Matrix3.fromHeadingPitchRoll = function(headingPitchRoll, result) { + //>>includeStart('debug', pragmas.debug); + if (!defined(headingPitchRoll)) { + throw new DeveloperError('headingPitchRoll is required'); + } + //>>includeEnd('debug'); + var cosTheta = Math.cos(-headingPitchRoll.pitch); + var cosPsi = Math.cos(-headingPitchRoll.heading); + var cosPhi = Math.cos(headingPitchRoll.roll); + var sinTheta = Math.sin(-headingPitchRoll.pitch); + var sinPsi = Math.sin(-headingPitchRoll.heading); + var sinPhi = Math.sin(headingPitchRoll.roll); + + var m00 = cosTheta * cosPsi; + var m01 = -cosPhi * sinPsi + sinPhi * sinTheta * cosPsi; + var m02 = sinPhi * sinPsi + cosPhi * sinTheta * cosPsi; + + var m10 = cosTheta * sinPsi; + var m11 = cosPhi * cosPsi + sinPhi * sinTheta * sinPsi; + var m12 = -sinTheta * cosPhi + cosPhi * sinTheta * sinPsi; + + var m20 = -sinTheta; + var m21 = sinPhi * cosTheta; + var m22 = cosPhi * cosTheta; + + if (!defined(result)) { + return new Matrix3(m00, m01, m02, + m10, m11, m12, + m20, m21, m22); + } + result[0] = m00; + result[1] = m10; + result[2] = m20; + result[3] = m01; + result[4] = m11; + result[5] = m21; + result[6] = m02; + result[7] = m12; + result[8] = m22; + return result; + }; + /** * Computes a Matrix3 instance representing a non-uniform scale. * diff --git a/Source/Core/Transforms.js b/Source/Core/Transforms.js index 74e326d264cf..ec44aedfd191 100644 --- a/Source/Core/Transforms.js +++ b/Source/Core/Transforms.js @@ -11,6 +11,7 @@ define([ './EarthOrientationParameters', './EarthOrientationParametersSample', './Ellipsoid', + './HeadingPitchRoll', './Iau2006XysData', './Iau2006XysSample', './JulianDate', @@ -31,6 +32,7 @@ define([ EarthOrientationParameters, EarthOrientationParametersSample, Ellipsoid, + HeadingPitchRoll, Iau2006XysData, Iau2006XysSample, JulianDate, @@ -347,6 +349,103 @@ define([ return result; }; + /** + * 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: + * + * + * @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 = function(origin, ellipsoid, result) { + //>>includeStart('debug', pragmas.debug); + if (!defined(origin)) { + throw new DeveloperError('origin is required.'); + } + //>>includeEnd('debug'); + + // If x and y are zero, assume origin is at a pole, which is a special case. + if (CesiumMath.equalsEpsilon(origin.x, 0.0, CesiumMath.EPSILON14) && + CesiumMath.equalsEpsilon(origin.y, 0.0, CesiumMath.EPSILON14)) { + var sign = CesiumMath.sign(origin.z); + if (!defined(result)) { + return new Matrix4( + -sign, 0.0, 0.0, origin.x, + 0.0, -1.0, 0.0, origin.y, + 0.0, 0.0, sign, origin.z, + 0.0, 0.0, 0.0, 1.0); + } + result[0] = -sign; + result[1] = 0.0; + result[2] = 0.0; + result[3] = 0.0; + result[4] = 0.0; + result[5] = -1.0; + result[6] = 0.0; + result[7] = 0.0; + result[8] = 0.0; + result[9] = 0.0; + result[10] = sign; + result[11] = 0.0; + result[12] = origin.x; + result[13] = origin.y; + result[14] = origin.z; + result[15] = 1.0; + return result; + } + + var normal = eastNorthUpToFixedFrameNormal;//Up + var tangent = eastNorthUpToFixedFrameTangent;//East + var bitangent = eastNorthUpToFixedFrameBitangent;//North + + ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); + ellipsoid.geodeticSurfaceNormal(origin, normal); + + tangent.x = -origin.y; + tangent.y = origin.x; + tangent.z = 0.0; + Cartesian3.normalize(tangent, tangent); + + Cartesian3.cross(normal, tangent, bitangent); + + if (!defined(result)) { + return new Matrix4( + bitangent.x, -tangent.x, normal.x, origin.x, + bitangent.y, -tangent.y, normal.y, origin.y, + bitangent.z, -tangent.z, normal.z, origin.z, + 0.0, 0.0, 0.0, 1.0); + } + result[0] = bitangent.x; + result[1] = bitangent.y; + result[2] = bitangent.z; + result[3] = 0.0; + result[4] = -tangent.x; + result[5] = -tangent.y; + result[6] = -tangent.z; + result[7] = 0.0; + result[8] = normal.x; + result[9] = normal.y; + result[10] = normal.z; + result[11] = 0.0; + result[12] = origin.x; + result[13] = origin.y; + result[14] = origin.z; + result[15] = 1.0; + return result; +}; + var scratchHPRQuaternion = new Quaternion(); var scratchScale = new Cartesian3(1.0, 1.0, 1.0); var scratchHPRMatrix4 = new Matrix4(); @@ -388,9 +487,7 @@ define([ * 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 {Number} heading The heading angle in radians. - * @param {Number} pitch The pitch angle in radians. - * @param {Number} roll The roll angle in radians. + * @param {HeadingPitchRoll} headingPitchRoll The heading, pitch, roll angles to apply. * @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. @@ -398,18 +495,25 @@ define([ * @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 transform = Cesium.Transforms.aircraftHeadingPitchRollToFixedFrame(center, heading, pitch, roll); - * - * @private + * var hpr = new HeadingPitchRoll(0.0, 0.0, 0.0); + * var hpr.heading = -Cesium.Math.PI_OVER_TWO; + * var hpr.pitch = Cesium.Math.PI_OVER_FOUR; + * var hpr.roll = 0.0; + * var transform = Cesium.Transforms.aircraftHeadingPitchRollToFixedFrame(center, hpr); */ - Transforms.aircraftHeadingPitchRollToFixedFrame = function(origin, heading, pitch, roll, ellipsoid, result) { + Transforms.aircraftHeadingPitchRollToFixedFrame = function(origin, headingPitchRoll, ellipsoid, result) { // checks for required parameters happen in the called functions - var hprQuaternion = Quaternion.fromHeadingPitchRoll(heading, pitch, roll, scratchHPRQuaternion); + //>>includeStart('debug', pragmas.debug); + if (!defined(origin)) { + throw new DeveloperError('origin is required.'); + } + if (!defined(headingPitchRoll)) { + throw new DeveloperError('headingPitchRoll is required.'); + } + //>>includeEnd('debug'); + var hprQuaternion = Quaternion.fromHeadingPitchRoll(headingPitchRoll.heading, headingPitchRoll.pitch, headingPitchRoll.roll, scratchHPRQuaternion); var hprMatrix = Matrix4.fromTranslationQuaternionRotationScale(Cartesian3.ZERO, hprQuaternion, scratchScale, scratchHPRMatrix4); - result = Transforms.northEastDownToFixedFrame(origin, ellipsoid, result); + result = Transforms.northWestUpToFixedFrame(origin, ellipsoid, result); return Matrix4.multiply(result, hprMatrix, result); }; @@ -452,9 +556,7 @@ define([ * 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 {Number} heading The heading angle in radians. - * @param {Number} pitch The pitch angle in radians. - * @param {Number} roll The roll angle in radians. + * @param {HeadingPitchRoll} headingPitchRoll The heading, pitch, roll angles to apply. * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid whose fixed frame is used in the transformation. * @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. @@ -462,16 +564,17 @@ define([ * @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 quaternion = Cesium.Transforms.aircraftHeadingPitchRollQuaternion(center, heading, pitch, roll); + * var hpr = new HeadingPitchRoll(0.0, 0.0, 0.0); + * var hpr.heading = -Cesium.Math.PI_OVER_TWO; + * var hpr.pitch = Cesium.Math.PI_OVER_FOUR; + * var hpr.roll = 0.0; + * var quaternion = Cesium.Transforms.aircraftHeadingPitchRollQuaternion(center, hpr); + * * - * @private */ - Transforms.aircraftHeadingPitchRollQuaternion = function(origin, heading, pitch, roll, ellipsoid, result) { + Transforms.aircraftHeadingPitchRollQuaternion = function(origin, headingPitchRoll, ellipsoid, result) { // checks for required parameters happen in the called functions - var transform = Transforms.aircraftHeadingPitchRollToFixedFrame(origin, heading, pitch, roll, ellipsoid, scratchENUMatrix4); + var transform = Transforms.aircraftHeadingPitchRollToFixedFrame(origin, headingPitchRoll, ellipsoid, scratchENUMatrix4); var rotation = Matrix4.getRotation(transform, scratchHPRMatrix3); return Quaternion.fromRotationMatrix(rotation, result); }; @@ -597,7 +700,7 @@ define([ * when(Cesium.Transforms.preloadIcrfFixed(interval), function() { * // the data is now loaded * }); - * + * * @see Transforms.computeIcrfToFixedMatrix * @see Transforms.computeFixedToIcrfMatrix * @see when @@ -638,7 +741,7 @@ define([ * camera.lookAtTransform(transform, offset); * } * }); - * + * * @see Transforms.preloadIcrfFixed */ Transforms.computeIcrfToFixedMatrix = function(date, result) { @@ -686,7 +789,7 @@ define([ * if (Cesium.defined(fixedToIcrf)) { * pointInInertial = Cesium.Matrix3.multiplyByVector(fixedToIcrf, pointInFixed, pointInInertial); * } - * + * * @see Transforms.preloadIcrfFixed */ Transforms.computeFixedToIcrfMatrix = function(date, result) { diff --git a/Specs/Core/HeadingPitchRollSpec.js b/Specs/Core/HeadingPitchRollSpec.js new file mode 100644 index 000000000000..cb0212267adf --- /dev/null +++ b/Specs/Core/HeadingPitchRollSpec.js @@ -0,0 +1,173 @@ +/*global defineSuite*/ +defineSuite([ + 'Core/HeadingPitchRoll', + 'Core/Math', + 'Core/Quaternion' +], function( + HeadingPitchRoll, + CesiumMath, + Quaternion) { + "use strict"; + /*global it,expect*/ + + var deg2rad = CesiumMath.RADIANS_PER_DEGREE; + + it('construct with default values', function() { + var headingPitchRoll = new HeadingPitchRoll(); + expect(headingPitchRoll.heading).toEqual(0.0); + expect(headingPitchRoll.pitch).toEqual(0.0); + expect(headingPitchRoll.roll).toEqual(0.0); + }); + + it('construct with all values', function() { + var headingPitchRoll = new HeadingPitchRoll(1.0 * deg2rad, 2.0 * deg2rad, 3.0 * deg2rad); + expect(headingPitchRoll.heading).toEqual(1.0 * deg2rad, 2.0 * deg2rad, 3.0 * deg2rad); + expect(headingPitchRoll.pitch).toEqual(2.0 * deg2rad, 2.0 * deg2rad, 3.0 * deg2rad); + expect(headingPitchRoll.roll).toEqual(3.0 * deg2rad, 2.0 * deg2rad, 3.0 * deg2rad); + }); + + it('conversion from quaternion', function() { + var testingTab = [ + [0, 0, 0], + [90 * deg2rad, 0, 0], + [-90 * deg2rad, 0, 0], + [0, 89 * deg2rad, 0], + [0, -89 * deg2rad, 0], + [0, 0, 90 * deg2rad], + [0, 0, -90 * deg2rad], + [30 * deg2rad, 30 * deg2rad, 30 * deg2rad], + [-30 * deg2rad, -30 * deg2rad, 45 * deg2rad] + ]; + for (var i = 0; i < testingTab.length; i++) { + var init = testingTab[i]; + var result = HeadingPitchRoll.fromQuaternion(Quaternion.fromHeadingPitchRoll(init[0], init[1], init[2])); + expect(init[0]).toEqualEpsilon(result.heading, CesiumMath.EPSILON11); + expect(init[1]).toEqualEpsilon(result.pitch, CesiumMath.EPSILON11); + expect(init[2]).toEqualEpsilon(result.roll, CesiumMath.EPSILON11); + } + }); + + it('conversion from degrees', function() { + var testingTab = [ + [0, 0, 0], + [90, 0, 0], + [-90, 0, 0], + [0, 89, 0], + [0, -89, 0], + [0, 0, 90], + [0, 0, -90], + [30, 30, 30], + [-30, -30, 45] + ]; + for (var i = 0; i < testingTab.length; i++) { + var init = testingTab[i]; + var result = HeadingPitchRoll.fromDegrees(init[0], init[1], init[2]); + expect(init[0] * deg2rad).toEqualEpsilon(result.heading, CesiumMath.EPSILON11); + expect(init[1] * deg2rad).toEqualEpsilon(result.pitch, CesiumMath.EPSILON11); + expect(init[2] * deg2rad).toEqualEpsilon(result.roll, CesiumMath.EPSILON11); + } + }); + + it('fromDegrees with result', function() { + var headingDeg = -115; + var pitchDeg = 37; + var rollDeg = 40; + var headingRad = headingDeg * deg2rad; + var pitchRad = pitchDeg * deg2rad; + var rollRad = rollDeg * deg2rad; + var result = new HeadingPitchRoll(); + var actual = HeadingPitchRoll.fromDegrees(headingDeg, pitchDeg, rollDeg, result); + var expected = new HeadingPitchRoll(headingRad, pitchRad, rollRad); + expect(actual).toEqual(expected); + expect(actual).toBe(result); + }); + + it('clone with a result parameter', function() { + var headingPitchRoll = new HeadingPitchRoll(1.0 * deg2rad, 2.0 * deg2rad, 3.0 * deg2rad); + var result = new HeadingPitchRoll(); + var returnedResult = HeadingPitchRoll.clone(headingPitchRoll, result); + expect(headingPitchRoll).not.toBe(result); + expect(result).toBe(returnedResult); + expect(headingPitchRoll).toEqual(result); + }); + + it('clone works with a result parameter that is an input parameter', function() { + var headingPitchRoll = new HeadingPitchRoll(1.0 * deg2rad, 2.0 * deg2rad, 3.0 * deg2rad); + var returnedResult = HeadingPitchRoll.clone(headingPitchRoll, headingPitchRoll); + expect(headingPitchRoll).toBe(returnedResult); + }); + + it('equals', function() { + var headingPitchRoll = new HeadingPitchRoll(1.0, 2.0, 3.0); + expect(HeadingPitchRoll.equals(headingPitchRoll, new HeadingPitchRoll(1.0, 2.0, 3.0))).toEqual(true); + expect(HeadingPitchRoll.equals(headingPitchRoll, new HeadingPitchRoll(2.0, 2.0, 3.0))).toEqual(false); + expect(HeadingPitchRoll.equals(headingPitchRoll, new HeadingPitchRoll(2.0, 1.0, 3.0))).toEqual(false); + expect(HeadingPitchRoll.equals(headingPitchRoll, new HeadingPitchRoll(1.0, 2.0, 4.0))).toEqual(false); + expect(HeadingPitchRoll.equals(headingPitchRoll, undefined)).toEqual(false); + }); + + it('equalsEpsilon', function() { + var headingPitchRoll = new HeadingPitchRoll(1.0, 2.0, 3.0); + expect(headingPitchRoll.equalsEpsilon(new HeadingPitchRoll(1.0, 2.0, 3.0), 0.0)).toEqual(true); + expect(headingPitchRoll.equalsEpsilon(new HeadingPitchRoll(1.0, 2.0, 3.0), 1.0)).toEqual(true); + expect(headingPitchRoll.equalsEpsilon(new HeadingPitchRoll(2.0, 2.0, 3.0), 1.0)).toEqual(true); + expect(headingPitchRoll.equalsEpsilon(new HeadingPitchRoll(1.0, 3.0, 3.0), 1.0)).toEqual(true); + expect(headingPitchRoll.equalsEpsilon(new HeadingPitchRoll(1.0, 2.0, 4.0), 1.0)).toEqual(true); + expect(headingPitchRoll.equalsEpsilon(new HeadingPitchRoll(2.0, 2.0, 3.0), CesiumMath.EPSILON6)).toEqual(false); + expect(headingPitchRoll.equalsEpsilon(new HeadingPitchRoll(1.0, 3.0, 3.0), CesiumMath.EPSILON6)).toEqual(false); + expect(headingPitchRoll.equalsEpsilon(new HeadingPitchRoll(1.0, 2.0, 4.0), CesiumMath.EPSILON6)).toEqual(false); + expect(headingPitchRoll.equalsEpsilon(undefined, 1)).toEqual(false); + + headingPitchRoll = new HeadingPitchRoll(3000000.0, 4000000.0, 5000000.0); + expect(headingPitchRoll.equalsEpsilon(new HeadingPitchRoll(3000000.0, 4000000.0, 5000000.0), 0.0)).toEqual(true); + expect(headingPitchRoll.equalsEpsilon(new HeadingPitchRoll(3000000.2, 4000000.0, 5000000.0), CesiumMath.EPSILON7)).toEqual(true); + expect(headingPitchRoll.equalsEpsilon(new HeadingPitchRoll(3000000.0, 4000000.2, 5000000.0), CesiumMath.EPSILON7)).toEqual(true); + expect(headingPitchRoll.equalsEpsilon(new HeadingPitchRoll(3000000.0, 4000000.0, 5000000.2), CesiumMath.EPSILON7)).toEqual(true); + expect(headingPitchRoll.equalsEpsilon(new HeadingPitchRoll(3000000.2, 4000000.2, 5000000.2), CesiumMath.EPSILON7)).toEqual(true); + expect(headingPitchRoll.equalsEpsilon(new HeadingPitchRoll(3000000.2, 4000000.2, 5000000.2), CesiumMath.EPSILON9)).toEqual(false); + expect(headingPitchRoll.equalsEpsilon(undefined, 1)).toEqual(false); + + expect(HeadingPitchRoll.equalsEpsilon(undefined, headingPitchRoll, 1)).toEqual(false); + }); + + it('toString', function() { + var headingPitchRoll = new HeadingPitchRoll(1.123, 2.345, 6.789); + expect(headingPitchRoll.toString()).toEqual('(1.123, 2.345, 6.789)'); + }); + + it('fromQuaternion throws with no parameter', function() { + expect(function() { + HeadingPitchRoll.fromQuaternion(); + }).toThrowDeveloperError(); + }); + + var scratchHeadingPitchRoll = new HeadingPitchRoll(); + + it('fromDegrees throws with no heading parameter', function() { + expect(function() { + HeadingPitchRoll.fromDegrees(undefined, 0, 0, scratchHeadingPitchRoll); + }).toThrowDeveloperError(); + expect(function() { + HeadingPitchRoll.fromDegrees(undefined, 0, 0); + }).toThrowDeveloperError(); + }); + + it('fromDegrees throws with no pitch parameter', function() { + expect(function() { + HeadingPitchRoll.fromDegrees(0, undefined, 0, scratchHeadingPitchRoll); + }).toThrowDeveloperError(); + expect(function() { + HeadingPitchRoll.fromDegrees(0, undefined, 0); + }).toThrowDeveloperError(); + }); + + it('fromDegrees throws with no roll parameter', function() { + expect(function() { + HeadingPitchRoll.fromDegrees(0, 0, undefined, scratchHeadingPitchRoll); + }).toThrowDeveloperError(); + expect(function() { + HeadingPitchRoll.fromDegrees(0, 0, undefined); + }).toThrowDeveloperError(); + }); + +}); diff --git a/Specs/Core/Matrix3Spec.js b/Specs/Core/Matrix3Spec.js index 0a190969b1cb..213593f49e66 100644 --- a/Specs/Core/Matrix3Spec.js +++ b/Specs/Core/Matrix3Spec.js @@ -1,14 +1,16 @@ /*global defineSuite*/ defineSuite([ - 'Core/Matrix3', - 'Core/Cartesian3', - 'Core/Math', - 'Core/Quaternion' - ], function( - Matrix3, - Cartesian3, - CesiumMath, - Quaternion) { + 'Core/Matrix3', + 'Core/Cartesian3', + 'Core/Math', + 'Core/HeadingPitchRoll', + 'Core/Quaternion' +], function( + Matrix3, + Cartesian3, + CesiumMath, + HeadingPitchRoll, + Quaternion) { 'use strict'; it('default constructor creates values array with all zeros.', function() { @@ -41,9 +43,9 @@ defineSuite([ it('can pack and unpack', function() { var array = []; var matrix = new Matrix3( - 1.0, 2.0, 3.0, - 4.0, 5.0, 6.0, - 7.0, 8.0, 9.0); + 1.0, 2.0, 3.0, + 4.0, 5.0, 6.0, + 7.0, 8.0, 9.0); Matrix3.pack(matrix, array); expect(array.length).toEqual(Matrix3.packedLength); expect(Matrix3.unpack(array)).toEqual(matrix); @@ -53,9 +55,9 @@ defineSuite([ var packed = new Array(3); var offset = 3; var matrix = new Matrix3( - 1.0, 2.0, 3.0, - 4.0, 5.0, 6.0, - 7.0, 8.0, 9.0); + 1.0, 2.0, 3.0, + 4.0, 5.0, 6.0, + 7.0, 8.0, 9.0); Matrix3.pack(matrix, packed, offset); expect(packed.length).toEqual(offset + Matrix3.packedLength); @@ -93,10 +95,10 @@ defineSuite([ var cPiOver2 = Math.cos(CesiumMath.PI_OVER_TWO); var tmp = Cartesian3.multiplyByScalar(new Cartesian3(0.0, 0.0, 1.0), sPiOver4, new Cartesian3()); - var quatnerion = new Quaternion(tmp.x, tmp.y, tmp.z, cPiOver4); + var quaternion = new Quaternion(tmp.x, tmp.y, tmp.z, cPiOver4); var expected = new Matrix3(cPiOver2, -sPiOver2, 0.0, sPiOver2, cPiOver2, 0.0, 0.0, 0.0, 1.0); - var returnedResult = Matrix3.fromQuaternion(quatnerion); + var returnedResult = Matrix3.fromQuaternion(quaternion); expect(returnedResult).toEqualEpsilon(expected, CesiumMath.EPSILON15); }); @@ -107,19 +109,50 @@ defineSuite([ var cPiOver2 = Math.cos(CesiumMath.PI_OVER_TWO); var tmp = Cartesian3.multiplyByScalar(new Cartesian3(0.0, 0.0, 1.0), sPiOver4, new Cartesian3()); - var quatnerion = new Quaternion(tmp.x, tmp.y, tmp.z, cPiOver4); + var quaternion = new Quaternion(tmp.x, tmp.y, tmp.z, cPiOver4); var expected = new Matrix3(cPiOver2, -sPiOver2, 0.0, sPiOver2, cPiOver2, 0.0, 0.0, 0.0, 1.0); var result = new Matrix3(); - var returnedResult = Matrix3.fromQuaternion(quatnerion, result); + var returnedResult = Matrix3.fromQuaternion(quaternion, result); + expect(result).toBe(returnedResult); + expect(returnedResult).toEqualEpsilon(expected, CesiumMath.EPSILON15); + }); + + it('fromHeadingPitchRoll works without a result parameter', function() { + var sPiOver4 = Math.sin(CesiumMath.PI_OVER_FOUR); + var cPiOver4 = Math.cos(CesiumMath.PI_OVER_FOUR); + var sPiOver2 = Math.sin(CesiumMath.PI_OVER_TWO); + var cPiOver2 = Math.cos(CesiumMath.PI_OVER_TWO); + + var tmp = Cartesian3.multiplyByScalar(new Cartesian3(0.0, 0.0, 1.0), sPiOver4, new Cartesian3()); + var quaternion = new Quaternion(tmp.x, tmp.y, tmp.z, cPiOver4); + var headingPitchRoll = HeadingPitchRoll.fromQuaternion(quaternion); + var expected = new Matrix3(cPiOver2, -sPiOver2, 0.0, sPiOver2, cPiOver2, 0.0, 0.0, 0.0, 1.0); + + var returnedResult = Matrix3.fromHeadingPitchRoll(headingPitchRoll); + expect(returnedResult).toEqualEpsilon(expected, CesiumMath.EPSILON15); + }); + + it('fromHeadingPitchRoll works with a result parameter', function() { + var sPiOver4 = Math.sin(CesiumMath.PI_OVER_FOUR); + var cPiOver4 = Math.cos(CesiumMath.PI_OVER_FOUR); + var sPiOver2 = Math.sin(CesiumMath.PI_OVER_TWO); + var cPiOver2 = Math.cos(CesiumMath.PI_OVER_TWO); + + var tmp = Cartesian3.multiplyByScalar(new Cartesian3(0.0, 0.0, 1.0), sPiOver4, new Cartesian3()); + var quaternion = new Quaternion(tmp.x, tmp.y, tmp.z, cPiOver4); + var headingPitchRoll = HeadingPitchRoll.fromQuaternion(quaternion); + var expected = new Matrix3(cPiOver2, -sPiOver2, 0.0, sPiOver2, cPiOver2, 0.0, 0.0, 0.0, 1.0); + var result = new Matrix3(); + var returnedResult = Matrix3.fromHeadingPitchRoll(headingPitchRoll, result); expect(result).toBe(returnedResult); expect(returnedResult).toEqualEpsilon(expected, CesiumMath.EPSILON15); }); it('fromScale works without a result parameter', function() { var expected = new Matrix3( - 7.0, 0.0, 0.0, - 0.0, 8.0, 0.0, - 0.0, 0.0, 9.0); + 7.0, 0.0, 0.0, + 0.0, 8.0, 0.0, + 0.0, 0.0, 9.0); var returnedResult = Matrix3.fromScale(new Cartesian3(7.0, 8.0, 9.0)); expect(returnedResult).not.toBe(expected); expect(returnedResult).toEqual(expected); @@ -127,9 +160,9 @@ defineSuite([ it('fromScale works with a result parameter', function() { var expected = new Matrix3( - 7.0, 0.0, 0.0, - 0.0, 8.0, 0.0, - 0.0, 0.0, 9.0); + 7.0, 0.0, 0.0, + 0.0, 8.0, 0.0, + 0.0, 0.0, 9.0); var result = new Matrix3(); var returnedResult = Matrix3.fromScale(new Cartesian3(7.0, 8.0, 9.0), result); expect(returnedResult).toBe(result); @@ -139,9 +172,9 @@ defineSuite([ it('fromUniformScale works without a result parameter', function() { var expected = new Matrix3( - 2.0, 0.0, 0.0, - 0.0, 2.0, 0.0, - 0.0, 0.0, 2.0); + 2.0, 0.0, 0.0, + 0.0, 2.0, 0.0, + 0.0, 0.0, 2.0); var returnedResult = Matrix3.fromUniformScale(2.0); expect(returnedResult).not.toBe(expected); expect(returnedResult).toEqual(expected); @@ -149,9 +182,9 @@ defineSuite([ it('fromUniformScale works with a result parameter', function() { var expected = new Matrix3( - 2.0, 0.0, 0.0, - 0.0, 2.0, 0.0, - 0.0, 0.0, 2.0); + 2.0, 0.0, 0.0, + 0.0, 2.0, 0.0, + 0.0, 0.0, 2.0); var result = new Matrix3(); var returnedResult = Matrix3.fromUniformScale(2.0, result); expect(returnedResult).toBe(result); @@ -160,9 +193,9 @@ defineSuite([ it('fromCrossProduct works without a result parameter', function() { var expected = new Matrix3( - 0.0, -3.0, -2.0, - 3.0, 0.0, -1.0, - 2.0, 1.0, 0.0); + 0.0, -3.0, -2.0, + 3.0, 0.0, -1.0, + 2.0, 1.0, 0.0); var left = new Cartesian3(1.0, -2.0, 3.0); var returnedResult = Matrix3.fromCrossProduct(left); expect(returnedResult).not.toBe(expected); @@ -183,9 +216,9 @@ defineSuite([ it('fromCrossProduct works with a result parameter', function() { var expected = new Matrix3( - 0.0, -3.0, -2.0, - 3.0, 0.0, -1.0, - 2.0, 1.0, 0.0); + 0.0, -3.0, -2.0, + 3.0, 0.0, -1.0, + 2.0, 1.0, 0.0); var left = new Cartesian3(1.0, -2.0, 3.0); var result = new Matrix3(); var returnedResult = Matrix3.fromCrossProduct(left, result); @@ -262,9 +295,9 @@ defineSuite([ it('fromRotationX works with a result parameter', function() { var expected = new Matrix3( - 1.0, 0.0, 0.0, - 0.0, 0.0, -1.0, - 0.0, 1.0, 0.0); + 1.0, 0.0, 0.0, + 0.0, 0.0, -1.0, + 0.0, 1.0, 0.0); var result = new Matrix3(); var matrix = Matrix3.fromRotationX(CesiumMath.toRadians(90.0), result); expect(matrix).toBe(result); @@ -284,9 +317,9 @@ defineSuite([ it('fromRotationY works with a result parameter', function() { var expected = new Matrix3( - 0.0, 0.0, 1.0, - 0.0, 1.0, 0.0, - -1.0, 0.0, 0.0); + 0.0, 0.0, 1.0, + 0.0, 1.0, 0.0, + -1.0, 0.0, 0.0); var result = new Matrix3(); var matrix = Matrix3.fromRotationY(CesiumMath.toRadians(90.0), result); expect(matrix).toBe(result); @@ -306,9 +339,9 @@ defineSuite([ it('fromRotationZ works with a result parameter', function() { var expected = new Matrix3( - 0.0, -1.0, 0.0, - 1.0, 0.0, 0.0, - 0.0, 0.0, 1.0); + 0.0, -1.0, 0.0, + 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0); var result = new Matrix3(); var matrix = Matrix3.fromRotationZ(CesiumMath.toRadians(90.0), result); expect(matrix).toBe(result); @@ -355,8 +388,8 @@ defineSuite([ it('getElementIndex works', function() { var i = 0; - for ( var col = 0; col < 3; col++) { - for ( var row = 0; row < 3; row++) { + for (var col = 0; col < 3; col++) { + for (var row = 0; row < 3; row++) { var index = Matrix3.getElementIndex(col, row); expect(index).toEqual(i); i++; @@ -633,13 +666,15 @@ defineSuite([ }); it('computes eigenvalues and eigenvectors', function() { - var a = new Matrix3(4.0, -1.0, 1.0, - -1.0, 3.0, -2.0, - 1.0, -2.0, 3.0); + var a = new Matrix3( + 4.0, -1.0, 1.0, + -1.0, 3.0, -2.0, + 1.0, -2.0, 3.0); - var expectedDiagonal = new Matrix3(3.0, 0.0, 0.0, - 0.0, 6.0, 0.0, - 0.0, 0.0, 1.0); + var expectedDiagonal = new Matrix3( + 3.0, 0.0, 0.0, + 0.0, 6.0, 0.0, + 0.0, 0.0, 1.0); var decomposition = Matrix3.computeEigenDecomposition(a); expect(decomposition.diagonal).toEqualEpsilon(expectedDiagonal, CesiumMath.EPSILON14); @@ -658,16 +693,18 @@ defineSuite([ }); it('computes eigenvalues and eigenvectors with result parameters', function() { - var a = new Matrix3(4.0, -1.0, 1.0, - -1.0, 3.0, -2.0, - 1.0, -2.0, 3.0); - - var expectedDiagonal = new Matrix3(3.0, 0.0, 0.0, - 0.0, 6.0, 0.0, - 0.0, 0.0, 1.0); + var a = new Matrix3( + 4.0, -1.0, 1.0, + -1.0, 3.0, -2.0, + 1.0, -2.0, 3.0); + + var expectedDiagonal = new Matrix3( + 3.0, 0.0, 0.0, + 0.0, 6.0, 0.0, + 0.0, 0.0, 1.0); var result = { - unitary : new Matrix3(), - diagonal : new Matrix3() + unitary: new Matrix3(), + diagonal: new Matrix3() }; var decomposition = Matrix3.computeEigenDecomposition(a, result); @@ -1031,6 +1068,12 @@ defineSuite([ }).toThrowDeveloperError(); }); + it('fromHeadingPitchRoll throws without quaternion parameter', function() { + expect(function() { + Matrix3.fromHeadingPitchRoll(undefined); + }).toThrowDeveloperError(); + }); + it('fromScale throws without scale parameter', function() { expect(function() { Matrix3.fromScale(undefined); @@ -1129,13 +1172,13 @@ defineSuite([ it('Matrix3 objects can be used as array like objects', function() { var matrix = new Matrix3( - 1, 4, 7, - 2, 5, 8, - 3, 6, 9); + 1, 4, 7, + 2, 5, 8, + 3, 6, 9); expect(matrix.length).toEqual(9); var intArray = new Uint32Array(matrix.length); intArray.set(matrix); - for ( var index = 0; index < matrix.length; index++) { + for (var index = 0; index < matrix.length; index++) { expect(intArray[index]).toEqual(index + 1); } }); diff --git a/Specs/Core/TransformsSpec.js b/Specs/Core/TransformsSpec.js index 032503c1bdfb..2fc0a2d48394 100644 --- a/Specs/Core/TransformsSpec.js +++ b/Specs/Core/TransformsSpec.js @@ -1,48 +1,53 @@ /*global defineSuite*/ defineSuite([ - 'Core/Transforms', - 'Core/Cartesian2', - 'Core/Cartesian3', - 'Core/Cartesian4', - 'Core/defined', - 'Core/DeveloperError', - 'Core/EarthOrientationParameters', - 'Core/Ellipsoid', - 'Core/GeographicProjection', - 'Core/Iau2006XysData', - 'Core/JulianDate', - 'Core/loadJson', - 'Core/Math', - 'Core/Matrix3', - 'Core/Matrix4', - 'Core/Quaternion', - 'Core/TimeConstants', - 'Core/TimeInterval', - 'ThirdParty/when' - ], function( - Transforms, - Cartesian2, - Cartesian3, - Cartesian4, - defined, - DeveloperError, - EarthOrientationParameters, - Ellipsoid, - GeographicProjection, - Iau2006XysData, - JulianDate, - loadJson, - CesiumMath, - Matrix3, - Matrix4, - Quaternion, - TimeConstants, - TimeInterval, - when) { + 'Core/Transforms', + 'Core/Cartesian2', + 'Core/Cartesian3', + 'Core/Cartesian4', + 'Core/defined', + 'Core/DeveloperError', + 'Core/EarthOrientationParameters', + 'Core/Ellipsoid', + 'Core/GeographicProjection', + 'Core/HeadingPitchRoll', + 'Core/Iau2006XysData', + 'Core/JulianDate', + 'Core/loadJson', + 'Core/Math', + 'Core/Matrix3', + 'Core/Matrix4', + 'Core/Quaternion', + 'Core/TimeConstants', + 'Core/TimeInterval', + 'ThirdParty/when' +], function( + Transforms, + Cartesian2, + Cartesian3, + Cartesian4, + defined, + DeveloperError, + EarthOrientationParameters, + Ellipsoid, + GeographicProjection, + HeadingPitchRoll, + Iau2006XysData, + JulianDate, + loadJson, + CesiumMath, + Matrix3, + Matrix4, + Quaternion, + TimeConstants, + TimeInterval, + when) { 'use strict'; var negativeX = new Cartesian4(-1, 0, 0, 0); + var negativeY = new Cartesian4(0, -1, 0, 0); var negativeZ = new Cartesian4(0, 0, -1, 0); + var scratchHeadingPitchRoll = new Quaternion(); + it('eastNorthUpToFixedFrame works without a result parameter', function() { var origin = new Cartesian3(1.0, 0.0, 0.0); var expectedTranslation = new Cartesian4(origin.x, origin.y, origin.z, 1.0); @@ -187,6 +192,54 @@ defineSuite([ expect(Matrix4.getColumn(returnedResult, 3, new Cartesian4())).toEqual(expectedTranslation); // translation }); + it('northWestUpToFixedFrame works without a result parameter', function() { + var origin = new Cartesian3(1.0, 0.0, 0.0); + var expectedTranslation = new Cartesian4(origin.x, origin.y, origin.z, 1.0); + + var returnedResult = Transforms.northWestUpToFixedFrame(origin, Ellipsoid.UNIT_SPHERE); + expect(Matrix4.getColumn(returnedResult, 0, new Cartesian4())).toEqual(Cartesian4.UNIT_Z); // north + expect(Matrix4.getColumn(returnedResult, 1, new Cartesian4())).toEqual(negativeY); // west + expect(Matrix4.getColumn(returnedResult, 2, new Cartesian4())).toEqual(Cartesian4.UNIT_X); // up + expect(Matrix4.getColumn(returnedResult, 3, new Cartesian4())).toEqual(expectedTranslation); // translation + }); + + it('northWestUpToFixedFrame works with a result parameter', function() { + var origin = new Cartesian3(1.0, 0.0, 0.0); + var expectedTranslation = new Cartesian4(origin.x, origin.y, origin.z, 1.0); + var result = new Matrix4(2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2); + + var returnedResult = Transforms.northWestUpToFixedFrame(origin, Ellipsoid.UNIT_SPHERE, result); + expect(result).toBe(returnedResult); + expect(Matrix4.getColumn(returnedResult, 0, new Cartesian4())).toEqual(Cartesian4.UNIT_Z); // north + expect(Matrix4.getColumn(returnedResult, 1, new Cartesian4())).toEqual(negativeY); // west + expect(Matrix4.getColumn(returnedResult, 2, new Cartesian4())).toEqual(Cartesian4.UNIT_X); // up + expect(Matrix4.getColumn(returnedResult, 3, new Cartesian4())).toEqual(expectedTranslation); // translation + }); + + it('northWestUpToFixedFrame works at the north pole', function() { + var northPole = new Cartesian3(0.0, 0.0, 1.0); + var expectedTranslation = new Cartesian4(northPole.x, northPole.y, northPole.z, 1.0); + + var result = new Matrix4(); + var returnedResult = Transforms.northWestUpToFixedFrame(northPole, Ellipsoid.UNIT_SPHERE, result); + expect(returnedResult).toBe(result); + expect(Matrix4.getColumn(returnedResult, 0, new Cartesian4())).toEqual(negativeX); // north + expect(Matrix4.getColumn(returnedResult, 1, new Cartesian4())).toEqual(negativeY); // west + expect(Matrix4.getColumn(returnedResult, 2, new Cartesian4())).toEqual(Cartesian4.UNIT_Z); // up + expect(Matrix4.getColumn(returnedResult, 3, new Cartesian4())).toEqual(expectedTranslation); // translation + }); + + it('northWestUpToFixedFrame works at the south pole', function() { + var southPole = new Cartesian3(0.0, 0.0, -1.0); + var expectedTranslation = new Cartesian4(southPole.x, southPole.y, southPole.z, 1.0); + + var returnedResult = Transforms.northWestUpToFixedFrame(southPole, Ellipsoid.UNIT_SPHERE); + expect(Matrix4.getColumn(returnedResult, 0, new Cartesian4())).toEqual(Cartesian4.UNIT_X); // north + expect(Matrix4.getColumn(returnedResult, 1, new Cartesian4())).toEqual(negativeY); // west + expect(Matrix4.getColumn(returnedResult, 2, new Cartesian4())).toEqual(negativeZ); // up + expect(Matrix4.getColumn(returnedResult, 3, new Cartesian4())).toEqual(expectedTranslation); // translation + }); + it('headingPitchRollToFixedFrame works without a result parameter', function() { var origin = new Cartesian3(1.0, 0.0, 0.0); var heading = CesiumMath.toRadians(20.0); @@ -254,11 +307,15 @@ defineSuite([ var expectedY = Matrix3.getColumn(expectedRotation, 1, new Cartesian3()); var expectedZ = Matrix3.getColumn(expectedRotation, 2, new Cartesian3()); - Cartesian3.fromElements(-expectedX.z, expectedX.y, expectedX.x, expectedX); - Cartesian3.fromElements(-expectedY.z, expectedY.y, expectedY.x, expectedY); - Cartesian3.fromElements(-expectedZ.z, expectedZ.y, expectedZ.x, expectedZ); + Cartesian3.fromElements(expectedX.z, -expectedX.y, expectedX.x, expectedX); + Cartesian3.fromElements(expectedY.z, -expectedY.y, expectedY.x, expectedY); + Cartesian3.fromElements(expectedZ.z, -expectedZ.y, expectedZ.x, expectedZ); - var returnedResult = Transforms.aircraftHeadingPitchRollToFixedFrame(origin, heading, pitch, roll, Ellipsoid.UNIT_SPHERE); + scratchHeadingPitchRoll.heading = heading; + scratchHeadingPitchRoll.pitch = pitch; + scratchHeadingPitchRoll.roll = roll; + + var returnedResult = Transforms.aircraftHeadingPitchRollToFixedFrame(origin, scratchHeadingPitchRoll, Ellipsoid.UNIT_SPHERE); var actualX = Cartesian3.fromCartesian4(Matrix4.getColumn(returnedResult, 0, new Cartesian4())); var actualY = Cartesian3.fromCartesian4(Matrix4.getColumn(returnedResult, 1, new Cartesian4())); var actualZ = Cartesian3.fromCartesian4(Matrix4.getColumn(returnedResult, 2, new Cartesian4())); @@ -281,12 +338,16 @@ defineSuite([ var expectedY = Matrix3.getColumn(expectedRotation, 1, new Cartesian3()); var expectedZ = Matrix3.getColumn(expectedRotation, 2, new Cartesian3()); - Cartesian3.fromElements(-expectedX.z, expectedX.y, expectedX.x, expectedX); - Cartesian3.fromElements(-expectedY.z, expectedY.y, expectedY.x, expectedY); - Cartesian3.fromElements(-expectedZ.z, expectedZ.y, expectedZ.x, expectedZ); + Cartesian3.fromElements(expectedX.z, -expectedX.y, expectedX.x, expectedX); + Cartesian3.fromElements(expectedY.z, -expectedY.y, expectedY.x, expectedY); + Cartesian3.fromElements(expectedZ.z, -expectedZ.y, expectedZ.x, expectedZ); + + scratchHeadingPitchRoll.heading = heading; + scratchHeadingPitchRoll.pitch = pitch; + scratchHeadingPitchRoll.roll = roll; var result = new Matrix4(); - var returnedResult = Transforms.aircraftHeadingPitchRollToFixedFrame(origin, heading, pitch, roll, Ellipsoid.UNIT_SPHERE, result); + var returnedResult = Transforms.aircraftHeadingPitchRollToFixedFrame(origin, scratchHeadingPitchRoll, Ellipsoid.UNIT_SPHERE, result); var actualX = Cartesian3.fromCartesian4(Matrix4.getColumn(returnedResult, 0, new Cartesian4())); var actualY = Cartesian3.fromCartesian4(Matrix4.getColumn(returnedResult, 1, new Cartesian4())); var actualZ = Cartesian3.fromCartesian4(Matrix4.getColumn(returnedResult, 2, new Cartesian4())); @@ -298,7 +359,7 @@ defineSuite([ expect(actualZ).toEqual(expectedZ); expect(actualTranslation).toEqual(origin); }); - + it('headingPitchRollQuaternion works without a result parameter', function() { var origin = new Cartesian3(1.0, 0.0, 0.0); var heading = CesiumMath.toRadians(20.0); @@ -331,29 +392,29 @@ defineSuite([ it('aircraftHeadingPitchRollQuaternion works without a result parameter', function() { var origin = new Cartesian3(1.0, 0.0, 0.0); - var heading = CesiumMath.toRadians(20.0); - var pitch = CesiumMath.toRadians(30.0); - var roll = CesiumMath.toRadians(40.0); + scratchHeadingPitchRoll.heading = CesiumMath.toRadians(20.0); + scratchHeadingPitchRoll.pitch = CesiumMath.toRadians(30.0); + scratchHeadingPitchRoll.roll = CesiumMath.toRadians(40.0); - var transform = Transforms.aircraftHeadingPitchRollToFixedFrame(origin, heading, pitch, roll, Ellipsoid.UNIT_SPHERE); + var transform = Transforms.aircraftHeadingPitchRollToFixedFrame(origin, scratchHeadingPitchRoll, Ellipsoid.UNIT_SPHERE); var expected = Matrix4.getRotation(transform, new Matrix3()); - var quaternion = Transforms.aircraftHeadingPitchRollQuaternion(origin, heading, pitch, roll, Ellipsoid.UNIT_SPHERE); + var quaternion = Transforms.aircraftHeadingPitchRollQuaternion(origin, scratchHeadingPitchRoll, Ellipsoid.UNIT_SPHERE); var actual = Matrix3.fromQuaternion(quaternion); expect(actual).toEqualEpsilon(expected, CesiumMath.EPSILON11); }); it('aircraftHeadingPitchRollQuaternion works with a result parameter', function() { var origin = new Cartesian3(1.0, 0.0, 0.0); - var heading = CesiumMath.toRadians(20.0); - var pitch = CesiumMath.toRadians(30.0); - var roll = CesiumMath.toRadians(40.0); + scratchHeadingPitchRoll.heading = CesiumMath.toRadians(20.0); + scratchHeadingPitchRoll.pitch = CesiumMath.toRadians(30.0); + scratchHeadingPitchRoll.roll = CesiumMath.toRadians(40.0); - var transform = Transforms.aircraftHeadingPitchRollToFixedFrame(origin, heading, pitch, roll, Ellipsoid.UNIT_SPHERE); + var transform = Transforms.aircraftHeadingPitchRollToFixedFrame(origin, scratchHeadingPitchRoll, Ellipsoid.UNIT_SPHERE); var expected = Matrix4.getRotation(transform, new Matrix3()); var result = new Quaternion(); - var quaternion = Transforms.aircraftHeadingPitchRollQuaternion(origin, heading, pitch, roll, Ellipsoid.UNIT_SPHERE, result); + var quaternion = Transforms.aircraftHeadingPitchRollQuaternion(origin, scratchHeadingPitchRoll, Ellipsoid.UNIT_SPHERE, result); var actual = Matrix3.fromQuaternion(quaternion); expect(quaternion).toBe(result); expect(actual).toEqualEpsilon(expected, CesiumMath.EPSILON11); @@ -426,8 +487,8 @@ defineSuite([ function preloadTransformationData(start, stop, eopDescription) { Transforms.earthOrientationParameters = new EarthOrientationParameters(eopDescription); var preloadInterval = new TimeInterval({ - start : start, - stop : stop + start: start, + stop: stop }); return Transforms.preloadIcrfFixed(preloadInterval); @@ -453,9 +514,9 @@ defineSuite([ var stop = JulianDate.fromIso8601(componentsData[componentsData.length - 1].date); return preloadTransformationData(start, stop, { - url : 'Data/EarthOrientationParameters/EOP-2011-July.json' + url: 'Data/EarthOrientationParameters/EOP-2011-July.json' }).then(function() { - for ( var i = 0; i < componentsData.length; ++i) { + for (var i = 0; i < componentsData.length; ++i) { var time = JulianDate.fromIso8601(componentsData[i].date); var resultT = new Matrix3(); var t = Transforms.computeIcrfToFixedMatrix(time, resultT); @@ -472,7 +533,7 @@ defineSuite([ var expectedMtx = Matrix3.fromQuaternion(Quaternion.conjugate(componentsData[i].icrfToFixedQuaternion, new Quaternion())); var testInverse = Matrix3.multiply(Matrix3.transpose(t, new Matrix3()), expectedMtx, new Matrix3()); var testDiff = new Matrix3(); - for ( var k = 0; k < 9; k++) { + for (var k = 0; k < 9; k++) { testDiff[k] = t[k] - expectedMtx[k]; } expect(testInverse).toEqualEpsilon(Matrix3.IDENTITY, CesiumMath.EPSILON14); @@ -487,7 +548,7 @@ defineSuite([ var time = new JulianDate(2455745, 43200); return preloadTransformationData(time, time, { - url : 'Data/EarthOrientationParameters/EOP-2011-July.json' + url: 'Data/EarthOrientationParameters/EOP-2011-July.json' }).then(function() { var resultT = new Matrix3(); var t = Transforms.computeIcrfToFixedMatrix(time, resultT); @@ -514,7 +575,7 @@ defineSuite([ var testInverse = Matrix3.multiply(Matrix3.transpose(t, new Matrix3()), expectedMtx, new Matrix3()); var testDiff = new Matrix3(); - for ( var i = 0; i < 9; i++) { + for (var i = 0; i < 9; i++) { testDiff[i] = t[i] - expectedMtx[i]; } expect(testInverse).toEqualEpsilon(Matrix3.IDENTITY, CesiumMath.EPSILON14); @@ -527,7 +588,7 @@ defineSuite([ var time = new JulianDate(2455745, 86395); return preloadTransformationData(time, time, { - url : 'Data/EarthOrientationParameters/EOP-2011-July.json' + url: 'Data/EarthOrientationParameters/EOP-2011-July.json' }).then(function() { var resultT = new Matrix3(); var t = Transforms.computeIcrfToFixedMatrix(time, resultT); @@ -537,7 +598,7 @@ defineSuite([ var testInverse = Matrix3.multiply(Matrix3.transpose(t, new Matrix3()), expectedMtx, new Matrix3()); var testDiff = new Matrix3(); - for ( var i = 0; i < 9; i++) { + for (var i = 0; i < 9; i++) { testDiff[i] = t[i] - expectedMtx[i]; } expect(testInverse).toEqualEpsilon(Matrix3.IDENTITY, CesiumMath.EPSILON14); @@ -549,7 +610,7 @@ defineSuite([ var time = new JulianDate(2455745, 10); return preloadTransformationData(time, time, { - url : 'Data/EarthOrientationParameters/EOP-2011-July.json' + url: 'Data/EarthOrientationParameters/EOP-2011-July.json' }).then(function() { var resultT = new Matrix3(); var t = Transforms.computeIcrfToFixedMatrix(time, resultT); @@ -559,7 +620,7 @@ defineSuite([ var testInverse = Matrix3.multiply(Matrix3.transpose(t, new Matrix3()), expectedMtx, new Matrix3()); var testDiff = new Matrix3(); - for ( var i = 0; i < 9; i++) { + for (var i = 0; i < 9; i++) { testDiff[i] = t[i] - expectedMtx[i]; } expect(testInverse).toEqualEpsilon(Matrix3.IDENTITY, CesiumMath.EPSILON14); @@ -577,7 +638,7 @@ defineSuite([ var time = new JulianDate(2455745, 43200); return preloadTransformationData(time, time, { - url : 'Data/EarthOrientationParameters/EOP-2011-July.json' + url: 'Data/EarthOrientationParameters/EOP-2011-July.json' }).then(function() { var resultT = new Matrix3(); var t = Transforms.computeIcrfToFixedMatrix(time, resultT); @@ -645,7 +706,7 @@ defineSuite([ var time = new JulianDate(2455745, 43200); return preloadTransformationData(time, time, { - url : 'Data/EarthOrientationParameters/EOP-Invalid.json' + url: 'Data/EarthOrientationParameters/EOP-Invalid.json' }).then(function() { expect(function() { return Transforms.computeIcrfToFixedMatrix(time); @@ -658,7 +719,7 @@ defineSuite([ var time = new JulianDate(2455745, 43200); return preloadTransformationData(time, time, { - url : 'Data/EarthOrientationParameters/EOP-DoesNotExist.json' + url: 'Data/EarthOrientationParameters/EOP-DoesNotExist.json' }).then(function() { expect(function() { return Transforms.computeIcrfToFixedMatrix(time); @@ -679,7 +740,7 @@ defineSuite([ return preloadTransformationData(time, time).then(function() { expect(Transforms.computeIcrfToFixedMatrix(time)).toBeDefined(); Transforms.earthOrientationParameters = new EarthOrientationParameters({ - url : 'Data/EarthOrientationParameters/EOP-2011-July.json' + url: 'Data/EarthOrientationParameters/EOP-2011-July.json' }); expect(Transforms.computeIcrfToFixedMatrix(time)).toBeUndefined(); }); @@ -690,15 +751,15 @@ defineSuite([ var height = 768.0; var perspective = Matrix4.computePerspectiveFieldOfView(CesiumMath.toRadians(60.0), width / height, 1.0, 10.0, new Matrix4()); var vpTransform = Matrix4.computeViewportTransformation({ - width : width, - height : height + width: width, + height: height }, 0, 1, new Matrix4()); it('pointToGLWindowCoordinates works at the center', function() { var view = Matrix4.fromCamera({ - position : Cartesian3.multiplyByScalar(Cartesian3.UNIT_X, 2.0, new Cartesian3()), - direction : Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3()), - up : Cartesian3.UNIT_Z + position: Cartesian3.multiplyByScalar(Cartesian3.UNIT_X, 2.0, new Cartesian3()), + direction: Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3()), + up: Cartesian3.UNIT_Z }); var mvpMatrix = Matrix4.multiply(perspective, view, new Matrix4()); @@ -709,9 +770,9 @@ defineSuite([ it('pointToGLWindowCoordinates works with a result parameter', function() { var view = Matrix4.fromCamera({ - position : Cartesian3.multiplyByScalar(Cartesian3.UNIT_X, 2.0, new Cartesian3()), - direction : Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3()), - up : Cartesian3.UNIT_Z + position: Cartesian3.multiplyByScalar(Cartesian3.UNIT_X, 2.0, new Cartesian3()), + direction: Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3()), + up: Cartesian3.UNIT_Z }); var mvpMatrix = Matrix4.multiply(perspective, view, new Matrix4()); @@ -746,9 +807,9 @@ defineSuite([ it('pointToWindowCoordinates works at the center', function() { var view = Matrix4.fromCamera({ - position : Cartesian3.multiplyByScalar(Cartesian3.UNIT_X, 2.0, new Cartesian3()), - direction : Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3()), - up : Cartesian3.UNIT_Z + position: Cartesian3.multiplyByScalar(Cartesian3.UNIT_X, 2.0, new Cartesian3()), + direction: Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3()), + up: Cartesian3.UNIT_Z }); var mvpMatrix = Matrix4.multiply(perspective, view, new Matrix4()); @@ -759,9 +820,9 @@ defineSuite([ it('pointToWindowCoordinates works with a result parameter', function() { var view = Matrix4.fromCamera({ - position : Cartesian3.multiplyByScalar(Cartesian3.UNIT_X, 2.0, new Cartesian3()), - direction : Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3()), - up : Cartesian3.UNIT_Z + position: Cartesian3.multiplyByScalar(Cartesian3.UNIT_X, 2.0, new Cartesian3()), + direction: Cartesian3.negate(Cartesian3.UNIT_X, new Cartesian3()), + up: Cartesian3.UNIT_Z }); var mvpMatrix = Matrix4.multiply(perspective, view, new Matrix4()); @@ -857,6 +918,12 @@ defineSuite([ }).toThrowDeveloperError(); }); + it('northWestUpToFixedFrame throws without an origin', function() { + expect(function() { + Transforms.northWestUpToFixedFrame(undefined, Ellipsoid.WGS84); + }).toThrowDeveloperError(); + }); + it('headingPitchRollToFixedFrame throws without an origin', function() { expect(function() { Transforms.headingPitchRollToFixedFrame(undefined, 0.0, 0.0, 0.0); @@ -883,49 +950,25 @@ defineSuite([ it('aircraftHeadingPitchRollToFixedFrame throws without an origin', function() { expect(function() { - Transforms.aircraftHeadingPitchRollToFixedFrame(undefined, 0.0, 0.0, 0.0); - }).toThrowDeveloperError(); - }); - - it('aircraftHeadingPitchRollToFixedFrame throws without an heading', function() { - expect(function() { - Transforms.aircraftHeadingPitchRollToFixedFrame(Cartesian3.ZERO, undefined, 0.0, 0.0); + Transforms.aircraftHeadingPitchRollToFixedFrame(undefined, scratchHeadingPitchRoll); }).toThrowDeveloperError(); }); - it('aircraftHeadingPitchRollToFixedFrame throws without an pitch', function() { + it('aircraftHeadingPitchRollToFixedFrame throws without an headingPitchRoll', function() { expect(function() { - Transforms.aircraftHeadingPitchRollToFixedFrame(Cartesian3.ZERO, 0.0, undefined, 0.0); + Transforms.aircraftHeadingPitchRollToFixedFrame(Cartesian3.ZERO, undefined); }).toThrowDeveloperError(); }); - it('aircraftHeadingPitchRollToFixedFrame throws without an roll', function() { - expect(function() { - Transforms.aircraftHeadingPitchRollToFixedFrame(Cartesian3.ZERO, 0.0, 0.0, undefined); - }).toThrowDeveloperError(); - }); - it('aircraftHeadingPitchRollQuaternion throws without an origin', function() { expect(function() { - Transforms.aircraftHeadingPitchRollQuaternion(undefined, 0.0, 0.0, 0.0); - }).toThrowDeveloperError(); - }); - - it('aircraftHeadingPitchRollQuaternion throws without an heading', function() { - expect(function() { - Transforms.aircraftHeadingPitchRollQuaternion(Cartesian3.ZERO, undefined, 0.0, 0.0); - }).toThrowDeveloperError(); - }); - - it('aircraftHeadingPitchRollQuaternion throws without an pitch', function() { - expect(function() { - Transforms.aircraftHeadingPitchRollQuaternion(Cartesian3.ZERO, 0.0, undefined, 0.0); + Transforms.aircraftHeadingPitchRollQuaternion(undefined, scratchHeadingPitchRoll); }).toThrowDeveloperError(); }); - it('aircraftHeadingPitchRollQuaternion throws without an roll', function() { + it('aircraftHeadingPitchRollQuaternion throws without an headingPitchRoll', function() { expect(function() { - Transforms.aircraftHeadingPitchRollQuaternion(Cartesian3.ZERO, 0.0, 0.0, undefined); + Transforms.aircraftHeadingPitchRollQuaternion(Cartesian3.ZERO, undefined); }).toThrowDeveloperError(); });