app/assets/javascripts/Workers/createExtentOutlineGeometry.js in cesium-0.24.0 vs app/assets/javascripts/Workers/createExtentOutlineGeometry.js in cesium-0.24.1

- old
+ new

@@ -1,7 +1,18849 @@ +/** + * Cesium - https://github.com/AnalyticalGraphicsInc/cesium + * + * Copyright 2011-2013 Cesium Contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Columbus View (Pat. Pend.) + * + * Portions licensed separately. + * See https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md for full licensing details. + */ +(function () { /*global define*/ -define(['Core/ExtentOutlineGeometry', 'Core/Ellipsoid', 'Core/Extent', 'Scene/PrimitivePipeline', 'Workers/createTaskProcessorWorker'], function( +define('Core/defined',[],function() { + "use strict"; + + /** + * Returns true if the object is defined, returns false otherwise. + * + * @exports defined + * + * @example + * if (defined(positions)) { + * doSomething(); + * } else { + * doSomethingElse(); + * } + */ + var defined = function(value) { + return value !== undefined; + }; + + return defined; +}); + +/*global define*/ +define('Core/freezeObject',['./defined'], function(defined) { + "use strict"; + + /** + * Freezes an object, using Object.freeze if available, otherwise returns + * the object unchanged. This function should be used in setup code to prevent + * errors from completely halting JavaScript execution in legacy browsers. + * + * @private + * + * @exports freezeObject + */ + var freezeObject = Object.freeze; + if (!defined(freezeObject)) { + freezeObject = function(o) { + return o; + }; + } + + return freezeObject; +}); +/*global define*/ +define('Core/defaultValue',[ + './freezeObject' + ], function( + freezeObject) { + "use strict"; + + /** + * Returns the first parameter if not undefined, otherwise the second parameter. + * Useful for setting a default value for a parameter. + * + * @exports defaultValue + * + * @example + * param = defaultValue(param, 'default'); + */ + var defaultValue = function(a, b) { + if (a !== undefined) { + return a; + } + return b; + }; + + /** + * A frozen empty object that can be used as the default value for options passed as + * an object literal. + */ + defaultValue.EMPTY_OBJECT = freezeObject({}); + + return defaultValue; +}); +/*global define*/ +define('Core/DeveloperError',['./defined'], function(defined) { + "use strict"; + + /** + * Constructs an exception object that is thrown due to a developer error, e.g., invalid argument, + * argument out of range, etc. This exception should only be thrown during development; + * it usually indicates a bug in the calling code. This exception should never be + * caught; instead the calling code should strive not to generate it. + * <br /><br /> + * On the other hand, a {@link RuntimeError} indicates an exception that may + * be thrown at runtime, e.g., out of memory, that the calling code should be prepared + * to catch. + * + * @alias DeveloperError + * + * @param {String} [message=undefined] The error message for this exception. + * + * @see RuntimeError + * @constructor + */ + var DeveloperError = function(message) { + /** + * 'DeveloperError' indicating that this exception was thrown due to a developer error. + * @type {String} + * @constant + */ + this.name = 'DeveloperError'; + + /** + * The explanation for why this exception was thrown. + * @type {String} + * @constant + */ + this.message = message; + + var e = new Error(); + + /** + * The stack trace of this exception, if available. + * @type {String} + * @constant + */ + this.stack = e.stack; + }; + + DeveloperError.prototype.toString = function() { + var str = this.name + ': ' + this.message; + + if (defined(this.stack)) { + str += '\n' + this.stack.toString(); + } + + return str; + }; + + return DeveloperError; +}); + +/*global define*/ +define('Core/Cartesian3',[ + './defaultValue', + './defined', + './DeveloperError', + './freezeObject' + ], function( + defaultValue, + defined, + DeveloperError, + freezeObject) { + "use strict"; + + /** + * A 3D Cartesian point. + * @alias Cartesian3 + * @constructor + * + * @param {Number} [x=0.0] The X component. + * @param {Number} [y=0.0] The Y component. + * @param {Number} [z=0.0] The Z component. + * + * @see Cartesian2 + * @see Cartesian4 + * @see Packable + */ + var Cartesian3 = function(x, y, z) { + /** + * The X component. + * @type {Number} + * @default 0.0 + */ + this.x = defaultValue(x, 0.0); + + /** + * The Y component. + * @type {Number} + * @default 0.0 + */ + this.y = defaultValue(y, 0.0); + + /** + * The Z component. + * @type {Number} + * @default 0.0 + */ + this.z = defaultValue(z, 0.0); + }; + + /** + * Converts the provided Spherical into Cartesian3 coordinates. + * @memberof Cartesian3 + * + * @param {Spherical} spherical The Spherical to be converted to Cartesian3. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} spherical is required. + */ + Cartesian3.fromSpherical = function(spherical, result) { + if (!defined(spherical)) { + throw new DeveloperError('spherical is required'); + } + + if (!defined(result)) { + result = new Cartesian3(); + } + var clock = spherical.clock; + var cone = spherical.cone; + var magnitude = defaultValue(spherical.magnitude, 1.0); + var radial = magnitude * Math.sin(cone); + result.x = radial * Math.cos(clock); + result.y = radial * Math.sin(clock); + result.z = magnitude * Math.cos(cone); + return result; + }; + + /** + * Creates a Cartesian3 instance from x, y and z coordinates. + * @memberof Cartesian3 + * + * @param {Number} x The x coordinate. + * @param {Number} y The y coordinate. + * @param {Number} z The z coordinate. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + */ + Cartesian3.fromElements = function(x, y, z, result) { + if (!defined(result)) { + return new Cartesian3(x, y, z); + } + + result.x = x; + result.y = y; + result.z = z; + return result; + }; + + /** + * Duplicates a Cartesian3 instance. + * @memberof Cartesian3 + * + * @param {Cartesian3} cartesian The Cartesian to duplicate. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. (Returns undefined if cartesian is undefined) + */ + Cartesian3.clone = function(cartesian, result) { + if (!defined(cartesian)) { + return undefined; + } + + if (!defined(result)) { + return new Cartesian3(cartesian.x, cartesian.y, cartesian.z); + } + + result.x = cartesian.x; + result.y = cartesian.y; + result.z = cartesian.z; + return result; + }; + + /** + * Creates a Cartesian3 instance from an existing Cartesian4. This simply takes the + * x, y, and z properties of the Cartesian4 and drops w. + * @function + * + * @param {Cartesian4} cartesian The Cartesian4 instance to create a Cartesian3 instance from. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian3.fromCartesian4 = Cartesian3.clone; + + /** + * The number of elements used to pack the object into an array. + * @Type {Number} + */ + Cartesian3.packedLength = 3; + + /** + * Stores the provided instance into the provided array. + * @memberof Cartesian3 + * + * @param {Cartesian3} value The value to pack. + * @param {Array} array The array to pack into. + * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. + * + * @exception {DeveloperError} value is required. + * @exception {DeveloperError} array is required. + */ + Cartesian3.pack = function(value, array, startingIndex) { + if (!defined(value)) { + throw new DeveloperError('value is required'); + } + + if (!defined(array)) { + throw new DeveloperError('array is required'); + } + + startingIndex = defaultValue(startingIndex, 0); + + array[startingIndex++] = value.x; + array[startingIndex++] = value.y; + array[startingIndex] = value.z; + }; + + /** + * Retrieves an instance from a packed array. + * @memberof Cartesian3 + * + * @param {Array} array The packed array. + * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. + * @param {Cartesian3} [result] The object into which to store the result. + * + * @exception {DeveloperError} array is required. + */ + Cartesian3.unpack = function(array, startingIndex, result) { + if (!defined(array)) { + throw new DeveloperError('array is required'); + } + + startingIndex = defaultValue(startingIndex, 0); + + if (!defined(result)) { + result = new Cartesian3(); + } + result.x = array[startingIndex++]; + result.y = array[startingIndex++]; + result.z = array[startingIndex]; + return result; + }; + + /** + * Creates a Cartesian3 from three consecutive elements in an array. + * @memberof Cartesian3 + * + * @param {Array} array The array whose three consecutive elements correspond to the x, y, and z components, respectively. + * @param {Number} [startingIndex=0] The offset into the array of the first element, which corresponds to the x component. + * @param {Cartesian3} [result] The object onto which to store the result. + * + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} array is required. + * + * @example + * // Create a Cartesian3 with (1.0, 2.0, 3.0) + * var v = [1.0, 2.0, 3.0]; + * var p = Cartesian3.fromArray(v); + * + * // Create a Cartesian3 with (1.0, 2.0, 3.0) using an offset into an array + * var v2 = [0.0, 0.0, 1.0, 2.0, 3.0]; + * var p2 = Cartesian3.fromArray(v2, 2); + */ + Cartesian3.fromArray = Cartesian3.unpack; + + /** + * Computes the value of the maximum component for the supplied Cartesian. + * @memberof Cartesian3 + * + * @param {Cartesian3} The cartesian to use. + * @returns {Number} The value of the maximum component. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian3.getMaximumComponent = function(cartesian) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + return Math.max(cartesian.x, cartesian.y, cartesian.z); + }; + + /** + * Computes the value of the minimum component for the supplied Cartesian. + * @memberof Cartesian3 + * + * @param {Cartesian3} The cartesian to use. + * @returns {Number} The value of the minimum component. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian3.getMinimumComponent = function(cartesian) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + return Math.min(cartesian.x, cartesian.y, cartesian.z); + }; + + /** + * Compares two Cartesians and computes a Cartesian which contains the minimum components of the supplied Cartesians. + * @memberof Cartesian3 + * + * @param {Cartesian3} first A cartesian to compare. + * @param {Cartesian3} second A cartesian to compare. + * @param {Cartesian3} [result] The object into which to store the result. + * @returns {Cartesian3} A cartesian with the minimum components. + * + * @exception {DeveloperError} first is required. + * @exception {DeveloperError} second is required. + */ + Cartesian3.getMinimumByComponent = function(first, second, result) { + if (!defined(first)) { + throw new DeveloperError('first is required.'); + } + if (!defined(second)) { + throw new DeveloperError('second is required.'); + } + + if (!defined(result)) { + result = new Cartesian3(); + } + + result.x = Math.min(first.x, second.x); + result.y = Math.min(first.y, second.y); + result.z = Math.min(first.z, second.z); + + return result; + }; + + /** + * Compares two Cartesians and computes a Cartesian which contains the maximum components of the supplied Cartesians. + * @memberof Cartesian3 + * + * @param {Cartesian3} first A cartesian to compare. + * @param {Cartesian3} second A cartesian to compare. + * @param {Cartesian3} [result] The object into which to store the result. + * @returns {Cartesian3} A cartesian with the maximum components. + * + * @exception {DeveloperError} first is required. + * @exception {DeveloperError} second is required. + */ + Cartesian3.getMaximumByComponent = function(first, second, result) { + if (!defined(first)) { + throw new DeveloperError('first is required.'); + } + if (!defined(second)) { + throw new DeveloperError('second is required.'); + } + + if (!defined(result)) { + result = new Cartesian3(); + } + + result.x = Math.max(first.x, second.x); + result.y = Math.max(first.y, second.y); + result.z = Math.max(first.z, second.z); + return result; + }; + + /** + * Computes the provided Cartesian's squared magnitude. + * @memberof Cartesian3 + * + * @param {Cartesian3} cartesian The Cartesian instance whose squared magnitude is to be computed. + * @returns {Number} The squared magnitude. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian3.magnitudeSquared = function(cartesian) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + return cartesian.x * cartesian.x + cartesian.y * cartesian.y + cartesian.z * cartesian.z; + }; + + /** + * Computes the Cartesian's magnitude (length). + * @memberof Cartesian3 + * + * @param {Cartesian3} cartesian The Cartesian instance whose magnitude is to be computed. + * @returns {Number} The magnitude. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian3.magnitude = function(cartesian) { + return Math.sqrt(Cartesian3.magnitudeSquared(cartesian)); + }; + + var distanceScratch = new Cartesian3(); + + /** + * Computes the distance between two points + * @memberof Cartesian3 + * + * @param {Cartesian3} left The first point to compute the distance from. + * @param {Cartesian3} right The second point to compute the distance to. + * + * @returns {Number} The distance between two points. + * + * @exception {DeveloperError} left and right are required. + * + * @example + * // Returns 1.0 + * var d = Cartesian3.distance(new Cartesian3(1.0, 0.0, 0.0), new Cartesian3(2.0, 0.0, 0.0)); + */ + Cartesian3.distance = function(left, right) { + if (!defined(left) || !defined(right)) { + throw new DeveloperError('left and right are required.'); + } + + Cartesian3.subtract(left, right, distanceScratch); + return Cartesian3.magnitude(distanceScratch); + }; + + /** + * Computes the normalized form of the supplied Cartesian. + * @memberof Cartesian3 + * + * @param {Cartesian3} cartesian The Cartesian to be normalized. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian3.normalize = function(cartesian, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + var magnitude = Cartesian3.magnitude(cartesian); + if (!defined(result)) { + return new Cartesian3(cartesian.x / magnitude, cartesian.y / magnitude, cartesian.z / magnitude); + } + result.x = cartesian.x / magnitude; + result.y = cartesian.y / magnitude; + result.z = cartesian.z / magnitude; + return result; + }; + + /** + * Computes the dot (scalar) product of two Cartesians. + * @memberof Cartesian3 + * + * @param {Cartesian3} left The first Cartesian. + * @param {Cartesian3} right The second Cartesian. + * @returns {Number} The dot product. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + */ + Cartesian3.dot = function(left, right) { + if (!defined(left)) { + throw new DeveloperError('left is required'); + } + if (!defined(right)) { + throw new DeveloperError('right is required'); + } + + return left.x * right.x + left.y * right.y + left.z * right.z; + }; + + /** + * Computes the componentwise product of two Cartesians. + * @memberof Cartesian3 + * + * @param {Cartesian3} left The first Cartesian. + * @param {Cartesian3} right The second Cartesian. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + */ + Cartesian3.multiplyComponents = function(left, right, result) { + if (!defined(left)) { + throw new DeveloperError('left is required'); + } + if (!defined(right)) { + throw new DeveloperError('right is required'); + } + + if (!defined(result)) { + return new Cartesian3(left.x * right.x, left.y * right.y, left.z * right.z); + } + result.x = left.x * right.x; + result.y = left.y * right.y; + result.z = left.z * right.z; + return result; + }; + + /** + * Computes the componentwise sum of two Cartesians. + * @memberof Cartesian3 + * + * @param {Cartesian3} left The first Cartesian. + * @param {Cartesian3} right The second Cartesian. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + */ + Cartesian3.add = function(left, right, result) { + if (!defined(left)) { + throw new DeveloperError('left is required'); + } + if (!defined(right)) { + throw new DeveloperError('right is required'); + } + + if (!defined(result)) { + return new Cartesian3(left.x + right.x, left.y + right.y, left.z + right.z); + } + result.x = left.x + right.x; + result.y = left.y + right.y; + result.z = left.z + right.z; + return result; + }; + + /** + * Computes the componentwise difference of two Cartesians. + * @memberof Cartesian3 + * + * @param {Cartesian3} left The first Cartesian. + * @param {Cartesian3} right The second Cartesian. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + */ + Cartesian3.subtract = function(left, right, result) { + if (!defined(left)) { + throw new DeveloperError('left is required'); + } + if (!defined(right)) { + throw new DeveloperError('right is required'); + } + + if (!defined(result)) { + return new Cartesian3(left.x - right.x, left.y - right.y, left.z - right.z); + } + result.x = left.x - right.x; + result.y = left.y - right.y; + result.z = left.z - right.z; + return result; + }; + + /** + * Multiplies the provided Cartesian componentwise by the provided scalar. + * @memberof Cartesian3 + * + * @param {Cartesian3} cartesian The Cartesian to be scaled. + * @param {Number} scalar The scalar to multiply with. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + * @exception {DeveloperError} scalar is required and must be a number. + */ + Cartesian3.multiplyByScalar = function(cartesian, scalar, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + if (typeof scalar !== 'number') { + throw new DeveloperError('scalar is required and must be a number.'); + } + + if (!defined(result)) { + return new Cartesian3(cartesian.x * scalar, cartesian.y * scalar, cartesian.z * scalar); + } + result.x = cartesian.x * scalar; + result.y = cartesian.y * scalar; + result.z = cartesian.z * scalar; + return result; + }; + + /** + * Divides the provided Cartesian componentwise by the provided scalar. + * @memberof Cartesian3 + * + * @param {Cartesian3} cartesian The Cartesian to be divided. + * @param {Number} scalar The scalar to divide by. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + * @exception {DeveloperError} scalar is required and must be a number. + */ + Cartesian3.divideByScalar = function(cartesian, scalar, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + if (typeof scalar !== 'number') { + throw new DeveloperError('scalar is required and must be a number.'); + } + + if (!defined(result)) { + return new Cartesian3(cartesian.x / scalar, cartesian.y / scalar, cartesian.z / scalar); + } + result.x = cartesian.x / scalar; + result.y = cartesian.y / scalar; + result.z = cartesian.z / scalar; + return result; + }; + + /** + * Negates the provided Cartesian. + * @memberof Cartesian3 + * + * @param {Cartesian3} cartesian The Cartesian to be negated. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian3.negate = function(cartesian, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + if (!defined(result)) { + return new Cartesian3(-cartesian.x, -cartesian.y, -cartesian.z); + } + result.x = -cartesian.x; + result.y = -cartesian.y; + result.z = -cartesian.z; + return result; + }; + + /** + * Computes the absolute value of the provided Cartesian. + * @memberof Cartesian3 + * + * @param {Cartesian3} cartesian The Cartesian whose absolute value is to be computed. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian3.abs = function(cartesian, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + if (!defined(result)) { + return new Cartesian3(Math.abs(cartesian.x), Math.abs(cartesian.y), Math.abs(cartesian.z)); + } + result.x = Math.abs(cartesian.x); + result.y = Math.abs(cartesian.y); + result.z = Math.abs(cartesian.z); + return result; + }; + + var lerpScratch = new Cartesian3(); + /** + * Computes the linear interpolation or extrapolation at t using the provided cartesians. + * @memberof Cartesian3 + * + * @param start The value corresponding to t at 0.0. + * @param end The value corresponding to t at 1.0. + * @param t The point along t at which to interpolate. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} start is required. + * @exception {DeveloperError} end is required. + * @exception {DeveloperError} t is required and must be a number. + */ + Cartesian3.lerp = function(start, end, t, result) { + if (!defined(start)) { + throw new DeveloperError('start is required.'); + } + if (!defined(end)) { + throw new DeveloperError('end is required.'); + } + if (typeof t !== 'number') { + throw new DeveloperError('t is required and must be a number.'); + } + + Cartesian3.multiplyByScalar(end, t, lerpScratch); + result = Cartesian3.multiplyByScalar(start, 1.0 - t, result); + return Cartesian3.add(lerpScratch, result, result); + }; + + var angleBetweenScratch = new Cartesian3(); + var angleBetweenScratch2 = new Cartesian3(); + /** + * Returns the angle, in radians, between the provided Cartesians. + * @memberof Cartesian3 + * + * @param {Cartesian3} left The first Cartesian. + * @param {Cartesian3} right The second Cartesian. + * @returns {Number} The angle between the Cartesians. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + */ + Cartesian3.angleBetween = function(left, right) { + if (!defined(left)) { + throw new DeveloperError('left is required'); + } + if (!defined(right)) { + throw new DeveloperError('right is required'); + } + + Cartesian3.normalize(left, angleBetweenScratch); + Cartesian3.normalize(right, angleBetweenScratch2); + var cosine = Cartesian3.dot(angleBetweenScratch, angleBetweenScratch2); + var sine = Cartesian3.magnitude(Cartesian3.cross(angleBetweenScratch, angleBetweenScratch2, angleBetweenScratch)); + return Math.atan2(sine, cosine); + }; + + var mostOrthogonalAxisScratch = new Cartesian3(); + /** + * Returns the axis that is most orthogonal to the provided Cartesian. + * @memberof Cartesian3 + * + * @param {Cartesian3} cartesian The Cartesian on which to find the most orthogonal axis. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The most orthogonal axis. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian3.mostOrthogonalAxis = function(cartesian, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required.'); + } + + var f = Cartesian3.normalize(cartesian, mostOrthogonalAxisScratch); + Cartesian3.abs(f, f); + + if (f.x <= f.y) { + if (f.x <= f.z) { + result = Cartesian3.clone(Cartesian3.UNIT_X, result); + } else { + result = Cartesian3.clone(Cartesian3.UNIT_Z, result); + } + } else { + if (f.y <= f.z) { + result = Cartesian3.clone(Cartesian3.UNIT_Y, result); + } else { + result = Cartesian3.clone(Cartesian3.UNIT_Z, result); + } + } + + return result; + }; + + /** + * Compares the provided Cartesians componentwise and returns + * <code>true</code> if they are equal, <code>false</code> otherwise. + * @memberof Cartesian3 + * + * @param {Cartesian3} [left] The first Cartesian. + * @param {Cartesian3} [right] The second Cartesian. + * @returns {Boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise. + */ + Cartesian3.equals = function(left, right) { + return (left === right) || + ((defined(left)) && + (defined(right)) && + (left.x === right.x) && + (left.y === right.y) && + (left.z === right.z)); + }; + + /** + * Compares the provided Cartesians componentwise and returns + * <code>true</code> if they are within the provided epsilon, + * <code>false</code> otherwise. + * @memberof Cartesian3 + * + * @param {Cartesian3} [left] The first Cartesian. + * @param {Cartesian3} [right] The second Cartesian. + * @param {Number} epsilon The epsilon to use for equality testing. + * @returns {Boolean} <code>true</code> if left and right are within the provided epsilon, <code>false</code> otherwise. + * + * @exception {DeveloperError} epsilon is required and must be a number. + */ + Cartesian3.equalsEpsilon = function(left, right, epsilon) { + if (typeof epsilon !== 'number') { + throw new DeveloperError('epsilon is required and must be a number.'); + } + + return (left === right) || + ((defined(left)) && + (defined(right)) && + (Math.abs(left.x - right.x) <= epsilon) && + (Math.abs(left.y - right.y) <= epsilon) && + (Math.abs(left.z - right.z) <= epsilon)); + }; + + /** + * Computes the cross (outer) product of two Cartesians. + * @memberof Cartesian3 + * + * @param {Cartesian3} left The first Cartesian. + * @param {Cartesian3} right The second Cartesian. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The cross product. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + */ + Cartesian3.cross = function(left, right, result) { + if (!defined(left)) { + throw new DeveloperError('left is required'); + } + if (!defined(right)) { + throw new DeveloperError('right is required'); + } + + var leftX = left.x; + var leftY = left.y; + var leftZ = left.z; + var rightX = right.x; + var rightY = right.y; + var rightZ = right.z; + + var x = leftY * rightZ - leftZ * rightY; + var y = leftZ * rightX - leftX * rightZ; + var z = leftX * rightY - leftY * rightX; + + if (!defined(result)) { + return new Cartesian3(x, y, z); + } + result.x = x; + result.y = y; + result.z = z; + return result; + }; + + /** + * An immutable Cartesian3 instance initialized to (0.0, 0.0, 0.0). + * @memberof Cartesian3 + */ + Cartesian3.ZERO = freezeObject(new Cartesian3(0.0, 0.0, 0.0)); + + /** + * An immutable Cartesian3 instance initialized to (1.0, 0.0, 0.0). + * @memberof Cartesian3 + */ + Cartesian3.UNIT_X = freezeObject(new Cartesian3(1.0, 0.0, 0.0)); + + /** + * An immutable Cartesian3 instance initialized to (0.0, 1.0, 0.0). + * @memberof Cartesian3 + */ + Cartesian3.UNIT_Y = freezeObject(new Cartesian3(0.0, 1.0, 0.0)); + + /** + * An immutable Cartesian3 instance initialized to (0.0, 0.0, 1.0). + * @memberof Cartesian3 + */ + Cartesian3.UNIT_Z = freezeObject(new Cartesian3(0.0, 0.0, 1.0)); + + /** + * Duplicates this Cartesian3 instance. + * @memberof Cartesian3 + * + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + */ + Cartesian3.prototype.clone = function(result) { + return Cartesian3.clone(this, result); + }; + + /** + * Compares this Cartesian against the provided Cartesian componentwise and returns + * <code>true</code> if they are equal, <code>false</code> otherwise. + * @memberof Cartesian3 + * + * @param {Cartesian3} [right] The right hand side Cartesian. + * @returns {Boolean} <code>true</code> if they are equal, <code>false</code> otherwise. + */ + Cartesian3.prototype.equals = function(right) { + return Cartesian3.equals(this, right); + }; + + /** + * Compares this Cartesian against the provided Cartesian componentwise and returns + * <code>true</code> if they are within the provided epsilon, + * <code>false</code> otherwise. + * @memberof Cartesian3 + * + * @param {Cartesian3} [right] The right hand side Cartesian. + * @param {Number} epsilon The epsilon to use for equality testing. + * @returns {Boolean} <code>true</code> if they are within the provided epsilon, <code>false</code> otherwise. + * + * @exception {DeveloperError} epsilon is required and must be a number. + */ + Cartesian3.prototype.equalsEpsilon = function(right, epsilon) { + return Cartesian3.equalsEpsilon(this, right, epsilon); + }; + + /** + * Creates a string representing this Cartesian in the format '(x, y, z)'. + * @memberof Cartesian3 + * + * @returns {String} A string representing this Cartesian in the format '(x, y, z)'. + */ + Cartesian3.prototype.toString = function() { + return '(' + this.x + ', ' + this.y + ', ' + this.z + ')'; + }; + + return Cartesian3; +}); + +/*global define*/ +define('Core/Cartesian4',[ + './defaultValue', + './defined', + './DeveloperError', + './freezeObject' + ], function( + defaultValue, + defined, + DeveloperError, + freezeObject) { + "use strict"; + + /** + * A 4D Cartesian point. + * @alias Cartesian4 + * @constructor + * + * @param {Number} [x=0.0] The X component. + * @param {Number} [y=0.0] The Y component. + * @param {Number} [z=0.0] The Z component. + * @param {Number} [w=0.0] The W component. + * + * @see Cartesian2 + * @see Cartesian3 + * @see Packable + */ + var Cartesian4 = function(x, y, z, w) { + /** + * The X component. + * @type {Number} + * @default 0.0 + */ + this.x = defaultValue(x, 0.0); + + /** + * The Y component. + * @type {Number} + * @default 0.0 + */ + this.y = defaultValue(y, 0.0); + + /** + * The Z component. + * @type {Number} + * @default 0.0 + */ + this.z = defaultValue(z, 0.0); + + /** + * The W component. + * @type {Number} + * @default 0.0 + */ + this.w = defaultValue(w, 0.0); + }; + + /** + * Creates a Cartesian4 instance from x, y, z and w coordinates. + * @memberof Cartesian4 + * + * @param {Number} x The x coordinate. + * @param {Number} y The y coordinate. + * @param {Number} z The z coordinate. + * @param {Number} w The w coordinate. + * @param {Cartesian4} [result] The object onto which to store the result. + * @returns {Cartesian4} The modified result parameter or a new Cartesian4 instance if one was not provided. + */ + Cartesian4.fromElements = function(x, y, z, w, result) { + if (!defined(result)) { + return new Cartesian4(x, y, z, w); + } + + result.x = x; + result.y = y; + result.z = z; + result.w = w; + return result; + }; + + /** + * Duplicates a Cartesian4 instance. + * @memberof Cartesian4 + * + * @param {Cartesian4} cartesian The Cartesian to duplicate. + * @param {Cartesian4} [result] The object onto which to store the result. + * @returns {Cartesian4} The modified result parameter or a new Cartesian4 instance if one was not provided. (Returns undefined if cartesian is undefined) + */ + Cartesian4.clone = function(cartesian, result) { + if (!defined(cartesian)) { + return undefined; + } + + if (!defined(result)) { + return new Cartesian4(cartesian.x, cartesian.y, cartesian.z, cartesian.w); + } + + result.x = cartesian.x; + result.y = cartesian.y; + result.z = cartesian.z; + result.w = cartesian.w; + return result; + }; + + + /** + * The number of elements used to pack the object into an array. + * @Type {Number} + */ + Cartesian4.packedLength = 4; + + /** + * Stores the provided instance into the provided array. + * @memberof Cartesian4 + * + * @param {Cartesian4} value The value to pack. + * @param {Array} array The array to pack into. + * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. + * + * @exception {DeveloperError} value is required. + * @exception {DeveloperError} array is required. + */ + Cartesian4.pack = function(value, array, startingIndex) { + if (!defined(value)) { + throw new DeveloperError('value is required'); + } + + if (!defined(array)) { + throw new DeveloperError('array is required'); + } + + startingIndex = defaultValue(startingIndex, 0); + + array[startingIndex++] = value.x; + array[startingIndex++] = value.y; + array[startingIndex++] = value.z; + array[startingIndex] = value.w; + }; + + /** + * Retrieves an instance from a packed array. + * @memberof Cartesian4 + * + * @param {Array} array The packed array. + * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. + * @param {Cartesian4} [result] The object into which to store the result. + * + * @exception {DeveloperError} array is required. + */ + Cartesian4.unpack = function(array, startingIndex, result) { + if (!defined(array)) { + throw new DeveloperError('array is required'); + } + + startingIndex = defaultValue(startingIndex, 0); + + if (!defined(result)) { + result = new Cartesian4(); + } + result.x = array[startingIndex++]; + result.y = array[startingIndex++]; + result.z = array[startingIndex++]; + result.w = array[startingIndex]; + return result; + }; + + + + /** + * Creates a Cartesian4 from four consecutive elements in an array. + * @memberof Cartesian4 + * + * @param {Array} array The array whose four consecutive elements correspond to the x, y, z, and w components, respectively. + * @param {Number} [startingIndex=0] The offset into the array of the first element, which corresponds to the x component. + * @param {Cartesian4} [result] The object onto which to store the result. + * + * @returns {Cartesian4} The modified result parameter or a new Cartesian4 instance if one was not provided. + * + * @exception {DeveloperError} array is required. + * + * @example + * // Create a Cartesian4 with (1.0, 2.0, 3.0, 4.0) + * var v = [1.0, 2.0, 3.0, 4.0]; + * var p = Cartesian4.fromArray(v); + * + * // Create a Cartesian4 with (1.0, 2.0, 3.0, 4.0) using an offset into an array + * var v2 = [0.0, 0.0, 1.0, 2.0, 3.0, 4.0]; + * var p2 = Cartesian4.fromArray(v2, 2); + */ + Cartesian4.fromArray = Cartesian4.unpack; + + /** + * Computes the value of the maximum component for the supplied Cartesian. + * @memberof Cartesian4 + * + * @param {Cartesian4} The cartesian to use. + * @returns {Number} The value of the maximum component. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian4.getMaximumComponent = function(cartesian) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + return Math.max(cartesian.x, cartesian.y, cartesian.z, cartesian.w); + }; + + /** + * Computes the value of the minimum component for the supplied Cartesian. + * @memberof Cartesian4 + * + * @param {Cartesian4} The cartesian to use. + * @returns {Number} The value of the minimum component. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian4.getMinimumComponent = function(cartesian) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + return Math.min(cartesian.x, cartesian.y, cartesian.z, cartesian.w); + }; + + /** + * Compares two Cartesians and computes a Cartesian which contains the minimum components of the supplied Cartesians. + * @memberof Cartesian4 + * + * @param {Cartesian4} first A cartesian to compare. + * @param {Cartesian4} second A cartesian to compare. + * @param {Cartesian4} [result] The object into which to store the result. + * @returns {Cartesian4} A cartesian with the minimum components. + * + * @exception {DeveloperError} first is required. + * @exception {DeveloperError} second is required. + */ + Cartesian4.getMinimumByComponent = function(first, second, result) { + if (!defined(first)) { + throw new DeveloperError('first is required.'); + } + if (!defined(second)) { + throw new DeveloperError('second is required.'); + } + + if (!defined(result)) { + result = new Cartesian4(); + } + + result.x = Math.min(first.x, second.x); + result.y = Math.min(first.y, second.y); + result.z = Math.min(first.z, second.z); + result.w = Math.min(first.w, second.w); + + return result; + }; + + /** + * Compares two Cartesians and computes a Cartesian which contains the maximum components of the supplied Cartesians. + * @memberof Cartesian4 + * + * @param {Cartesian4} first A cartesian to compare. + * @param {Cartesian4} second A cartesian to compare. + * @param {Cartesian4} [result] The object into which to store the result. + * @returns {Cartesian4} A cartesian with the maximum components. + * + * @exception {DeveloperError} first is required. + * @exception {DeveloperError} second is required. + */ + Cartesian4.getMaximumByComponent = function(first, second, result) { + if (!defined(first)) { + throw new DeveloperError('first is required.'); + } + if (!defined(second)) { + throw new DeveloperError('second is required.'); + } + + if (!defined(result)) { + result = new Cartesian4(); + } + + result.x = Math.max(first.x, second.x); + result.y = Math.max(first.y, second.y); + result.z = Math.max(first.z, second.z); + result.w = Math.max(first.w, second.w); + + return result; + }; + + /** + * Computes the provided Cartesian's squared magnitude. + * @memberof Cartesian4 + * + * @param {Cartesian4} cartesian The Cartesian instance whose squared magnitude is to be computed. + * @returns {Number} The squared magnitude. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian4.magnitudeSquared = function(cartesian) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + return cartesian.x * cartesian.x + cartesian.y * cartesian.y + cartesian.z * cartesian.z + cartesian.w * cartesian.w; + }; + + /** + * Computes the Cartesian's magnitude (length). + * @memberof Cartesian4 + * + * @param {Cartesian4} cartesian The Cartesian instance whose magnitude is to be computed. + * @returns {Number} The magnitude. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian4.magnitude = function(cartesian) { + return Math.sqrt(Cartesian4.magnitudeSquared(cartesian)); + }; + + var distanceScratch = new Cartesian4(); + + /** + * Computes the 4-space distance between two points + * @memberof Cartesian4 + * + * @param {Cartesian4} left The first point to compute the distance from. + * @param {Cartesian4} right The second point to compute the distance to. + * + * @returns {Number} The distance between two points. + * + * @exception {DeveloperError} left and right are required. + * + * @example + * // Returns 1.0 + * var d = Cartesian4.distance(new Cartesian4(1.0, 0.0, 0.0, 0.0), new Cartesian4(2.0, 0.0, 0.0, 0.0)); + */ + Cartesian4.distance = function(left, right) { + if (!defined(left) || !defined(right)) { + throw new DeveloperError('left and right are required.'); + } + + Cartesian4.subtract(left, right, distanceScratch); + return Cartesian4.magnitude(distanceScratch); + }; + + /** + * Computes the normalized form of the supplied Cartesian. + * @memberof Cartesian4 + * + * @param {Cartesian4} cartesian The Cartesian to be normalized. + * @param {Cartesian4} [result] The object onto which to store the result. + * @returns {Cartesian4} The modified result parameter or a new Cartesian4 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian4.normalize = function(cartesian, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + var magnitude = Cartesian4.magnitude(cartesian); + if (!defined(result)) { + return new Cartesian4(cartesian.x / magnitude, cartesian.y / magnitude, cartesian.z / magnitude, cartesian.w / magnitude); + } + result.x = cartesian.x / magnitude; + result.y = cartesian.y / magnitude; + result.z = cartesian.z / magnitude; + result.w = cartesian.w / magnitude; + return result; + }; + + /** + * Computes the dot (scalar) product of two Cartesians. + * @memberof Cartesian4 + * + * @param {Cartesian4} left The first Cartesian. + * @param {Cartesian4} right The second Cartesian. + * @returns {Number} The dot product. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + */ + Cartesian4.dot = function(left, right) { + if (!defined(left)) { + throw new DeveloperError('left is required'); + } + if (!defined(right)) { + throw new DeveloperError('right is required'); + } + + return left.x * right.x + left.y * right.y + left.z * right.z + left.w * right.w; + }; + + /** + * Computes the componentwise product of two Cartesians. + * @memberof Cartesian4 + * + * @param {Cartesian4} left The first Cartesian. + * @param {Cartesian4} right The second Cartesian. + * @param {Cartesian4} [result] The object onto which to store the result. + * @returns {Cartesian4} The modified result parameter or a new Cartesian4 instance if one was not provided. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + */ + Cartesian4.multiplyComponents = function(left, right, result) { + if (!defined(left)) { + throw new DeveloperError('left is required'); + } + if (!defined(right)) { + throw new DeveloperError('right is required'); + } + + if (!defined(result)) { + return new Cartesian4(left.x * right.x, left.y * right.y, left.z * right.z, left.w * right.w); + } + result.x = left.x * right.x; + result.y = left.y * right.y; + result.z = left.z * right.z; + result.w = left.w * right.w; + return result; + }; + + /** + * Computes the componentwise sum of two Cartesians. + * @memberof Cartesian4 + * + * @param {Cartesian4} left The first Cartesian. + * @param {Cartesian4} right The second Cartesian. + * @param {Cartesian4} [result] The object onto which to store the result. + * @returns {Cartesian4} The modified result parameter or a new Cartesian4 instance if one was not provided. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + */ + Cartesian4.add = function(left, right, result) { + if (!defined(left)) { + throw new DeveloperError('left is required'); + } + if (!defined(right)) { + throw new DeveloperError('right is required'); + } + + if (!defined(result)) { + return new Cartesian4(left.x + right.x, left.y + right.y, left.z + right.z, left.w + right.w); + } + result.x = left.x + right.x; + result.y = left.y + right.y; + result.z = left.z + right.z; + result.w = left.w + right.w; + return result; + }; + + /** + * Computes the componentwise difference of two Cartesians. + * @memberof Cartesian4 + * + * @param {Cartesian4} left The first Cartesian. + * @param {Cartesian4} right The second Cartesian. + * @param {Cartesian4} [result] The object onto which to store the result. + * @returns {Cartesian4} The modified result parameter or a new Cartesian4 instance if one was not provided. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + */ + Cartesian4.subtract = function(left, right, result) { + if (!defined(left)) { + throw new DeveloperError('left is required'); + } + if (!defined(right)) { + throw new DeveloperError('right is required'); + } + + if (!defined(result)) { + return new Cartesian4(left.x - right.x, left.y - right.y, left.z - right.z, left.w - right.w); + } + result.x = left.x - right.x; + result.y = left.y - right.y; + result.z = left.z - right.z; + result.w = left.w - right.w; + return result; + }; + + /** + * Multiplies the provided Cartesian componentwise by the provided scalar. + * @memberof Cartesian4 + * + * @param {Cartesian4} cartesian The Cartesian to be scaled. + * @param {Number} scalar The scalar to multiply with. + * @param {Cartesian4} [result] The object onto which to store the result. + * @returns {Cartesian4} The modified result parameter or a new Cartesian4 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + * @exception {DeveloperError} scalar is required and must be a number. + */ + Cartesian4.multiplyByScalar = function(cartesian, scalar, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + if (typeof scalar !== 'number') { + throw new DeveloperError('scalar is required and must be a number.'); + } + + if (!defined(result)) { + return new Cartesian4(cartesian.x * scalar, cartesian.y * scalar, cartesian.z * scalar, cartesian.w * scalar); + } + result.x = cartesian.x * scalar; + result.y = cartesian.y * scalar; + result.z = cartesian.z * scalar; + result.w = cartesian.w * scalar; + return result; + }; + + /** + * Divides the provided Cartesian componentwise by the provided scalar. + * @memberof Cartesian4 + * + * @param {Cartesian4} cartesian The Cartesian to be divided. + * @param {Number} scalar The scalar to divide by. + * @param {Cartesian4} [result] The object onto which to store the result. + * @returns {Cartesian4} The modified result parameter or a new Cartesian4 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + * @exception {DeveloperError} scalar is required and must be a number. + */ + Cartesian4.divideByScalar = function(cartesian, scalar, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + if (typeof scalar !== 'number') { + throw new DeveloperError('scalar is required and must be a number.'); + } + + if (!defined(result)) { + return new Cartesian4(cartesian.x / scalar, cartesian.y / scalar, cartesian.z / scalar, cartesian.w / scalar); + } + result.x = cartesian.x / scalar; + result.y = cartesian.y / scalar; + result.z = cartesian.z / scalar; + result.w = cartesian.w / scalar; + return result; + }; + + /** + * Negates the provided Cartesian. + * @memberof Cartesian4 + * + * @param {Cartesian4} cartesian The Cartesian to be negated. + * @param {Cartesian4} [result] The object onto which to store the result. + * @returns {Cartesian4} The modified result parameter or a new Cartesian4 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian4.negate = function(cartesian, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + if (!defined(result)) { + return new Cartesian4(-cartesian.x, -cartesian.y, -cartesian.z, -cartesian.w); + } + result.x = -cartesian.x; + result.y = -cartesian.y; + result.z = -cartesian.z; + result.w = -cartesian.w; + return result; + }; + + /** + * Computes the absolute value of the provided Cartesian. + * @memberof Cartesian4 + * + * @param {Cartesian4} cartesian The Cartesian whose absolute value is to be computed. + * @param {Cartesian4} [result] The object onto which to store the result. + * @returns {Cartesian4} The modified result parameter or a new Cartesian4 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian4.abs = function(cartesian, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + if (!defined(result)) { + return new Cartesian4(Math.abs(cartesian.x), Math.abs(cartesian.y), Math.abs(cartesian.z), Math.abs(cartesian.w)); + } + result.x = Math.abs(cartesian.x); + result.y = Math.abs(cartesian.y); + result.z = Math.abs(cartesian.z); + result.w = Math.abs(cartesian.w); + return result; + }; + + var lerpScratch = new Cartesian4(); + /** + * Computes the linear interpolation or extrapolation at t using the provided cartesians. + * @memberof Cartesian4 + * + * @param start The value corresponding to t at 0.0. + * @param end The value corresponding to t at 1.0. + * @param t The point along t at which to interpolate. + * @param {Cartesian4} [result] The object onto which to store the result. + * @returns {Cartesian4} The modified result parameter or a new Cartesian4 instance if one was not provided. + * + * @exception {DeveloperError} start is required. + * @exception {DeveloperError} end is required. + * @exception {DeveloperError} t is required and must be a number. + */ + Cartesian4.lerp = function(start, end, t, result) { + if (!defined(start)) { + throw new DeveloperError('start is required.'); + } + if (!defined(end)) { + throw new DeveloperError('end is required.'); + } + if (typeof t !== 'number') { + throw new DeveloperError('t is required and must be a number.'); + } + + Cartesian4.multiplyByScalar(end, t, lerpScratch); + result = Cartesian4.multiplyByScalar(start, 1.0 - t, result); + return Cartesian4.add(lerpScratch, result, result); + }; + + var mostOrthogonalAxisScratch = new Cartesian4(); + /** + * Returns the axis that is most orthogonal to the provided Cartesian. + * @memberof Cartesian4 + * + * @param {Cartesian4} cartesian The Cartesian on which to find the most orthogonal axis. + * @param {Cartesian4} [result] The object onto which to store the result. + * @returns {Cartesian4} The most orthogonal axis. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian4.mostOrthogonalAxis = function(cartesian, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required.'); + } + + var f = Cartesian4.normalize(cartesian, mostOrthogonalAxisScratch); + Cartesian4.abs(f, f); + + if (f.x <= f.y) { + if (f.x <= f.z) { + if (f.x <= f.w) { + result = Cartesian4.clone(Cartesian4.UNIT_X, result); + } else { + result = Cartesian4.clone(Cartesian4.UNIT_W, result); + } + } else if (f.z <= f.w) { + result = Cartesian4.clone(Cartesian4.UNIT_Z, result); + } else { + result = Cartesian4.clone(Cartesian4.UNIT_W, result); + } + } else if (f.y <= f.z) { + if (f.y <= f.w) { + result = Cartesian4.clone(Cartesian4.UNIT_Y, result); + } else { + result = Cartesian4.clone(Cartesian4.UNIT_W, result); + } + } else if (f.z <= f.w) { + result = Cartesian4.clone(Cartesian4.UNIT_Z, result); + } else { + result = Cartesian4.clone(Cartesian4.UNIT_W, result); + } + + return result; + }; + + /** + * Compares the provided Cartesians componentwise and returns + * <code>true</code> if they are equal, <code>false</code> otherwise. + * @memberof Cartesian4 + * + * @param {Cartesian4} [left] The first Cartesian. + * @param {Cartesian4} [right] The second Cartesian. + * @returns {Boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise. + */ + Cartesian4.equals = function(left, right) { + return (left === right) || + ((defined(left)) && + (defined(right)) && + (left.x === right.x) && + (left.y === right.y) && + (left.z === right.z) && + (left.w === right.w)); + }; + + /** + * Compares the provided Cartesians componentwise and returns + * <code>true</code> if they are within the provided epsilon, + * <code>false</code> otherwise. + * @memberof Cartesian4 + * + * @param {Cartesian4} [left] The first Cartesian. + * @param {Cartesian4} [right] The second Cartesian. + * @param {Number} epsilon The epsilon to use for equality testing. + * @returns {Boolean} <code>true</code> if left and right are within the provided epsilon, <code>false</code> otherwise. + * + * @exception {DeveloperError} epsilon is required and must be a number. + */ + Cartesian4.equalsEpsilon = function(left, right, epsilon) { + if (typeof epsilon !== 'number') { + throw new DeveloperError('epsilon is required and must be a number.'); + } + + return (left === right) || + ((defined(left)) && + (defined(right)) && + (Math.abs(left.x - right.x) <= epsilon) && + (Math.abs(left.y - right.y) <= epsilon) && + (Math.abs(left.z - right.z) <= epsilon) && + (Math.abs(left.w - right.w) <= epsilon)); + }; + + /** + * An immutable Cartesian4 instance initialized to (0.0, 0.0, 0.0, 0.0). + * @memberof Cartesian4 + */ + Cartesian4.ZERO = freezeObject(new Cartesian4(0.0, 0.0, 0.0, 0.0)); + + /** + * An immutable Cartesian4 instance initialized to (1.0, 0.0, 0.0, 0.0). + * @memberof Cartesian4 + */ + Cartesian4.UNIT_X = freezeObject(new Cartesian4(1.0, 0.0, 0.0, 0.0)); + + /** + * An immutable Cartesian4 instance initialized to (0.0, 1.0, 0.0, 0.0). + * @memberof Cartesian4 + */ + Cartesian4.UNIT_Y = freezeObject(new Cartesian4(0.0, 1.0, 0.0, 0.0)); + + /** + * An immutable Cartesian4 instance initialized to (0.0, 0.0, 1.0, 0.0). + * @memberof Cartesian4 + */ + Cartesian4.UNIT_Z = freezeObject(new Cartesian4(0.0, 0.0, 1.0, 0.0)); + + /** + * An immutable Cartesian4 instance initialized to (0.0, 0.0, 0.0, 1.0). + * @memberof Cartesian4 + */ + Cartesian4.UNIT_W = freezeObject(new Cartesian4(0.0, 0.0, 0.0, 1.0)); + + /** + * Duplicates this Cartesian4 instance. + * @memberof Cartesian4 + * + * @param {Cartesian4} [result] The object onto which to store the result. + * @returns {Cartesian4} The modified result parameter or a new Cartesian4 instance if one was not provided. + */ + Cartesian4.prototype.clone = function(result) { + return Cartesian4.clone(this, result); + }; + + /** + * Compares this Cartesian against the provided Cartesian componentwise and returns + * <code>true</code> if they are equal, <code>false</code> otherwise. + * @memberof Cartesian4 + * + * @param {Cartesian4} [right] The right hand side Cartesian. + * @returns {Boolean} <code>true</code> if they are equal, <code>false</code> otherwise. + */ + Cartesian4.prototype.equals = function(right) { + return Cartesian4.equals(this, right); + }; + + /** + * Compares this Cartesian against the provided Cartesian componentwise and returns + * <code>true</code> if they are within the provided epsilon, + * <code>false</code> otherwise. + * @memberof Cartesian4 + * + * @param {Cartesian4} [right] The right hand side Cartesian. + * @param {Number} epsilon The epsilon to use for equality testing. + * @returns {Boolean} <code>true</code> if they are within the provided epsilon, <code>false</code> otherwise. + * + * @exception {DeveloperError} epsilon is required and must be a number. + */ + Cartesian4.prototype.equalsEpsilon = function(right, epsilon) { + return Cartesian4.equalsEpsilon(this, right, epsilon); + }; + + /** + * Creates a string representing this Cartesian in the format '(x, y)'. + * @memberof Cartesian4 + * + * @returns {String} A string representing the provided Cartesian in the format '(x, y)'. + */ + Cartesian4.prototype.toString = function() { + return '(' + this.x + ', ' + this.y + ', ' + this.z + ', ' + this.w + ')'; + }; + + return Cartesian4; +}); + +/* + I've wrapped Makoto Matsumoto and Takuji Nishimura's code in a namespace + so it's better encapsulated. Now you can have multiple random number generators + and they won't stomp all over eachother's state. + + If you want to use this as a substitute for Math.random(), use the random() + method like so: + + var m = new MersenneTwister(); + var randomNumber = m.random(); + + You can also call the other genrand_{foo}() methods on the instance. + + If you want to use a specific seed in order to get a repeatable random + sequence, pass an integer into the constructor: + + var m = new MersenneTwister(123); + + and that will always produce the same random sequence. + + Sean McCullough (banksean@gmail.com) +*/ + +/* + A C-program for MT19937, with initialization improved 2002/1/26. + Coded by Takuji Nishimura and Makoto Matsumoto. + + Before using, initialize the state by using init_genrand(seed) + or init_by_array(init_key, key_length). +*/ +/** +@license +mersenne-twister.js - https://gist.github.com/banksean/300494 + + Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura, + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The names of its contributors may not be used to endorse or promote + products derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +/* + Any feedback is very welcome. + http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html + email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) +*/ +define('ThirdParty/mersenne-twister',[],function() { +var MersenneTwister = function(seed) { + if (seed == undefined) { + seed = new Date().getTime(); + } + /* Period parameters */ + this.N = 624; + this.M = 397; + this.MATRIX_A = 0x9908b0df; /* constant vector a */ + this.UPPER_MASK = 0x80000000; /* most significant w-r bits */ + this.LOWER_MASK = 0x7fffffff; /* least significant r bits */ + + this.mt = new Array(this.N); /* the array for the state vector */ + this.mti=this.N+1; /* mti==N+1 means mt[N] is not initialized */ + + this.init_genrand(seed); +} + +/* initializes mt[N] with a seed */ +MersenneTwister.prototype.init_genrand = function(s) { + this.mt[0] = s >>> 0; + for (this.mti=1; this.mti<this.N; this.mti++) { + var s = this.mt[this.mti-1] ^ (this.mt[this.mti-1] >>> 30); + this.mt[this.mti] = (((((s & 0xffff0000) >>> 16) * 1812433253) << 16) + (s & 0x0000ffff) * 1812433253) + + this.mti; + /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */ + /* In the previous versions, MSBs of the seed affect */ + /* only MSBs of the array mt[]. */ + /* 2002/01/09 modified by Makoto Matsumoto */ + this.mt[this.mti] >>>= 0; + /* for >32 bit machines */ + } +} + +/* initialize by an array with array-length */ +/* init_key is the array for initializing keys */ +/* key_length is its length */ +/* slight change for C++, 2004/2/26 */ +//MersenneTwister.prototype.init_by_array = function(init_key, key_length) { +// var i, j, k; +// this.init_genrand(19650218); +// i=1; j=0; +// k = (this.N>key_length ? this.N : key_length); +// for (; k; k--) { +// var s = this.mt[i-1] ^ (this.mt[i-1] >>> 30) +// this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1664525) << 16) + ((s & 0x0000ffff) * 1664525))) +// + init_key[j] + j; /* non linear */ +// this.mt[i] >>>= 0; /* for WORDSIZE > 32 machines */ +// i++; j++; +// if (i>=this.N) { this.mt[0] = this.mt[this.N-1]; i=1; } +// if (j>=key_length) j=0; +// } +// for (k=this.N-1; k; k--) { +// var s = this.mt[i-1] ^ (this.mt[i-1] >>> 30); +// this.mt[i] = (this.mt[i] ^ (((((s & 0xffff0000) >>> 16) * 1566083941) << 16) + (s & 0x0000ffff) * 1566083941)) +// - i; /* non linear */ +// this.mt[i] >>>= 0; /* for WORDSIZE > 32 machines */ +// i++; +// if (i>=this.N) { this.mt[0] = this.mt[this.N-1]; i=1; } +// } +// +// this.mt[0] = 0x80000000; /* MSB is 1; assuring non-zero initial array */ +//} + +/* generates a random number on [0,0xffffffff]-interval */ +MersenneTwister.prototype.genrand_int32 = function() { + var y; + var mag01 = new Array(0x0, this.MATRIX_A); + /* mag01[x] = x * MATRIX_A for x=0,1 */ + + if (this.mti >= this.N) { /* generate N words at one time */ + var kk; + + if (this.mti == this.N+1) /* if init_genrand() has not been called, */ + this.init_genrand(5489); /* a default initial seed is used */ + + for (kk=0;kk<this.N-this.M;kk++) { + y = (this.mt[kk]&this.UPPER_MASK)|(this.mt[kk+1]&this.LOWER_MASK); + this.mt[kk] = this.mt[kk+this.M] ^ (y >>> 1) ^ mag01[y & 0x1]; + } + for (;kk<this.N-1;kk++) { + y = (this.mt[kk]&this.UPPER_MASK)|(this.mt[kk+1]&this.LOWER_MASK); + this.mt[kk] = this.mt[kk+(this.M-this.N)] ^ (y >>> 1) ^ mag01[y & 0x1]; + } + y = (this.mt[this.N-1]&this.UPPER_MASK)|(this.mt[0]&this.LOWER_MASK); + this.mt[this.N-1] = this.mt[this.M-1] ^ (y >>> 1) ^ mag01[y & 0x1]; + + this.mti = 0; + } + + y = this.mt[this.mti++]; + + /* Tempering */ + y ^= (y >>> 11); + y ^= (y << 7) & 0x9d2c5680; + y ^= (y << 15) & 0xefc60000; + y ^= (y >>> 18); + + return y >>> 0; +} + +/* generates a random number on [0,0x7fffffff]-interval */ +//MersenneTwister.prototype.genrand_int31 = function() { +// return (this.genrand_int32()>>>1); +//} + +/* generates a random number on [0,1]-real-interval */ +//MersenneTwister.prototype.genrand_real1 = function() { +// return this.genrand_int32()*(1.0/4294967295.0); +// /* divided by 2^32-1 */ +//} + +/* generates a random number on [0,1)-real-interval */ +MersenneTwister.prototype.random = function() { + return this.genrand_int32()*(1.0/4294967296.0); + /* divided by 2^32 */ +} + +/* generates a random number on (0,1)-real-interval */ +//MersenneTwister.prototype.genrand_real3 = function() { +// return (this.genrand_int32() + 0.5)*(1.0/4294967296.0); +// /* divided by 2^32 */ +//} + +/* generates a random number on [0,1) with 53-bit resolution*/ +//MersenneTwister.prototype.genrand_res53 = function() { +// var a=this.genrand_int32()>>>5, b=this.genrand_int32()>>>6; +// return(a*67108864.0+b)*(1.0/9007199254740992.0); +//} + +/* These real versions are due to Isaku Wada, 2002/01/09 added */ + +return MersenneTwister; +}); +/*global define*/ +define('Core/Math',[ + './defaultValue', + './defined', + './DeveloperError', + '../ThirdParty/mersenne-twister' + ], function( + defaultValue, + defined, + DeveloperError, + MersenneTwister) { + "use strict"; + + /** + * Math functions. + * @exports CesiumMath + */ + var CesiumMath = {}; + + /** + * 0.1 + * @type {Number} + * @constant + */ + CesiumMath.EPSILON1 = 0.1; + + /** + * 0.01 + * @type {Number} + * @constant + */ + CesiumMath.EPSILON2 = 0.01; + + /** + * 0.001 + * @type {Number} + * @constant + */ + CesiumMath.EPSILON3 = 0.001; + + /** + * 0.0001 + * @type {Number} + * @constant + */ + CesiumMath.EPSILON4 = 0.0001; + + /** + * 0.00001 + * @type {Number} + * @constant + */ + CesiumMath.EPSILON5 = 0.00001; + + /** + * 0.000001 + * @type {Number} + * @constant + */ + CesiumMath.EPSILON6 = 0.000001; + + /** + * 0.0000001 + * @type {Number} + * @constant + */ + CesiumMath.EPSILON7 = 0.0000001; + + /** + * 0.00000001 + * @type {Number} + * @constant + */ + CesiumMath.EPSILON8 = 0.00000001; + + /** + * 0.000000001 + * @type {Number} + * @constant + */ + CesiumMath.EPSILON9 = 0.000000001; + + /** + * 0.0000000001 + * @type {Number} + * @constant + */ + CesiumMath.EPSILON10 = 0.0000000001; + + /** + * 0.00000000001 + * @type {Number} + * @constant + */ + CesiumMath.EPSILON11 = 0.00000000001; + + /** + * 0.000000000001 + * @type {Number} + * @constant + */ + CesiumMath.EPSILON12 = 0.000000000001; + + /** + * 0.0000000000001 + * @type {Number} + * @constant + */ + CesiumMath.EPSILON13 = 0.0000000000001; + + /** + * 0.00000000000001 + * @type {Number} + * @constant + */ + CesiumMath.EPSILON14 = 0.00000000000001; + + /** + * 0.000000000000001 + * @type {Number} + * @constant + */ + CesiumMath.EPSILON15 = 0.000000000000001; + + /** + * 0.0000000000000001 + * @type {Number} + * @constant + */ + CesiumMath.EPSILON16 = 0.0000000000000001; + + /** + * 0.00000000000000001 + * @type {Number} + * @constant + */ + CesiumMath.EPSILON17 = 0.00000000000000001; + + /** + * 0.000000000000000001 + * @type {Number} + * @constant + */ + CesiumMath.EPSILON18 = 0.000000000000000001; + + /** + * 0.0000000000000000001 + * @type {Number} + * @constant + */ + CesiumMath.EPSILON19 = 0.0000000000000000001; + + /** + * 0.00000000000000000001 + * @type {Number} + * @constant + */ + CesiumMath.EPSILON20 = 0.00000000000000000001; + + /** + * 3.986004418e14 + * @type {Number} + * @constant + */ + CesiumMath.GRAVITATIONALPARAMETER = 3.986004418e14; + + /** + * Radius of the sun in meters: 6.955e8 + * @type {Number} + * @constant + */ + CesiumMath.SOLAR_RADIUS = 6.955e8; + + /** + * The mean radius of the moon, according to the "Report of the IAU/IAG Working Group on + * Cartographic Coordinates and Rotational Elements of the Planets and satellites: 2000", + * Celestial Mechanics 82: 83-110, 2002. + * @type {Number} + * @constant + */ + CesiumMath.LUNAR_RADIUS = 1737400.0; + + /** + * 64 * 1024 + * @type {Number} + * @constant + */ + CesiumMath.SIXTY_FOUR_KILOBYTES = 64 * 1024; + + /** + * Returns the sign of the value; 1 if the value is positive, -1 if the value is + * negative, or 0 if the value is 0. + * + * @param {Number} value The value to return the sign of. + * + * @returns {Number} The sign of value. + */ + CesiumMath.sign = function(value) { + if (value > 0) { + return 1; + } + if (value < 0) { + return -1; + } + + return 0; + }; + + /** + * Returns the hyperbolic sine of a {@code Number}. + * The hyperbolic sine of <em>value</em> is defined to be + * (<em>e<sup>x</sup>&nbsp;-&nbsp;e<sup>-x</sup></em>)/2.0 + * where <i>e</i> is Euler's number, approximately 2.71828183. + * + * <p>Special cases: + * <ul> + * <li>If the argument is NaN, then the result is NaN.</li> + * + * <li>If the argument is infinite, then the result is an infinity + * with the same sign as the argument.</li> + * + * <li>If the argument is zero, then the result is a zero with the + * same sign as the argument.</li> + * </ul> + *</p> + * + * @param value The number whose hyperbolic sine is to be returned. + * + * @returns The hyperbolic sine of {@code value}. + * + */ + CesiumMath.sinh = function(value) { + var part1 = Math.pow(Math.E, value); + var part2 = Math.pow(Math.E, -1.0 * value); + + return (part1 - part2) * 0.5; + }; + + /** + * Returns the hyperbolic cosine of a {@code Number}. + * The hyperbolic cosine of <strong>value</strong> is defined to be + * (<em>e<sup>x</sup>&nbsp;+&nbsp;e<sup>-x</sup></em>)/2.0 + * where <i>e</i> is Euler's number, approximately 2.71828183. + * + * <p>Special cases: + * <ul> + * <li>If the argument is NaN, then the result is NaN.</li> + * + * <li>If the argument is infinite, then the result is positive infinity.</li> + * + * <li>If the argument is zero, then the result is {@code 1.0}.</li> + * </ul> + *</p> + * + * @param value The number whose hyperbolic cosine is to be returned. + * + * @returns The hyperbolic cosine of {@code value}. + */ + CesiumMath.cosh = function(value) { + var part1 = Math.pow(Math.E, value); + var part2 = Math.pow(Math.E, -1.0 * value); + + return (part1 + part2) * 0.5; + }; + + /** + * DOC_TBA + */ + CesiumMath.lerp = function(p, q, time) { + return ((1.0 - time) * p) + (time * q); + }; + + /** + * pi + * + * @type {Number} + * @constant + * @see czm_pi + */ + CesiumMath.PI = Math.PI; + + /** + * 1/pi + * + * @type {Number} + * @constant + * @see czm_oneOverPi + */ + CesiumMath.ONE_OVER_PI = 1.0 / Math.PI; + + /** + * pi/2 + * + * @type {Number} + * @constant + * @see czm_piOverTwo + */ + CesiumMath.PI_OVER_TWO = Math.PI * 0.5; + + /** + * pi/3 + * + * @type {Number} + * @constant + * @see czm_piOverThree + */ + CesiumMath.PI_OVER_THREE = Math.PI / 3.0; + + /** + * pi/4 + * + * @type {Number} + * @constant + * @see czm_piOverFour + */ + CesiumMath.PI_OVER_FOUR = Math.PI / 4.0; + + /** + * pi/6 + * + * @type {Number} + * @constant + * @see czm_piOverSix + */ + CesiumMath.PI_OVER_SIX = Math.PI / 6.0; + + /** + * 3pi/2 + * + * @type {Number} + * @constant + * @see czm_threePiOver2 + */ + CesiumMath.THREE_PI_OVER_TWO = (3.0 * Math.PI) * 0.5; + + /** + * 2pi + * + * @type {Number} + * @constant + * @see czm_twoPi + */ + CesiumMath.TWO_PI = 2.0 * Math.PI; + + /** + * 1/2pi + * + * @type {Number} + * @constant + * @see czm_oneOverTwoPi + */ + CesiumMath.ONE_OVER_TWO_PI = 1.0 / (2.0 * Math.PI); + + /** + * The number of radians in a degree. + * + * @type {Number} + * @constant + * @default Math.PI / 180.0 + * @see czm_radiansPerDegree + */ + CesiumMath.RADIANS_PER_DEGREE = Math.PI / 180.0; + + /** + * The number of degrees in a radian. + * + * @type {Number} + * @constant + * @default 180.0 / Math.PI + * @see czm_degreesPerRadian + */ + CesiumMath.DEGREES_PER_RADIAN = 180.0 / Math.PI; + + /** + * The number of radians in an arc second. + * + * @type {Number} + * @constant + * @default {@link CesiumMath.RADIANS_PER_DEGREE} / 3600.0 + * @see czm_radiansPerArcSecond + */ + CesiumMath.RADIANS_PER_ARCSECOND = CesiumMath.RADIANS_PER_DEGREE / 3600.0; + + /** + * Converts degrees to radians. + * @param {Number} degrees The angle to convert in degrees. + * @returns {Number} The corresponding angle in radians. + */ + CesiumMath.toRadians = function(degrees) { + return degrees * CesiumMath.RADIANS_PER_DEGREE; + }; + + /** + * Converts radians to degrees. + * @param {Number} radians The angle to convert in radians. + * @returns {Number} The corresponding angle in degrees. + */ + CesiumMath.toDegrees = function(radians) { + return radians * CesiumMath.DEGREES_PER_RADIAN; + }; + + /** + * Converts a longitude value, in radians, to the range [<code>-Math.PI</code>, <code>Math.PI</code>). + * + * @param {Number} angle The longitude value, in radians, to convert to the range [<code>-Math.PI</code>, <code>Math.PI</code>). + * + * @returns {Number} The equivalent longitude value in the range [<code>-Math.PI</code>, <code>Math.PI</code>). + * + * @example + * // Convert 270 degrees to -90 degrees longitude + * var longitude = CesiumMath.convertLongitudeRange(CesiumMath.toRadians(270.0)); + */ + CesiumMath.convertLongitudeRange = function(angle) { + var twoPi = CesiumMath.TWO_PI; + + var simplified = angle - Math.floor(angle / twoPi) * twoPi; + + if (simplified < -Math.PI) { + return simplified + twoPi; + } + if (simplified >= Math.PI) { + return simplified - twoPi; + } + + return simplified; + }; + + /** + * Produces an angle in the range 0 <= angle <= 2Pi which is equivalent to the provided angle. + * @param {Number} angle in radians + * @returns {Number} The angle in the range ()<code>-CesiumMath.PI</code>, <code>CesiumMath.PI</code>). + */ + CesiumMath.negativePiToPi = function(x) { + var epsilon10 = CesiumMath.EPSILON10; + var pi = CesiumMath.PI; + var two_pi = CesiumMath.TWO_PI; + while (x < -(pi + epsilon10)) { + x += two_pi; + } + if (x < -pi) { + return -pi; + } + while (x > pi + epsilon10) { + x -= two_pi; + } + return x > pi ? pi : x; + }; + + /** + * Produces an angle in the range -Pi <= angle <= Pi which is equivalent to the provided angle. + * @param {Number} angle in radians + * @returns {Number} The angle in the range (0 , <code>CesiumMath.TWO_PI</code>). + */ + CesiumMath.zeroToTwoPi = function(x) { + var value = x % CesiumMath.TWO_PI; + // We do a second modules here if we add 2Pi to ensure that we don't have any numerical issues with very + // small negative values. + return (value < 0.0) ? (value + CesiumMath.TWO_PI) % CesiumMath.TWO_PI : value; + }; + + /** + * DOC_TBA + */ + CesiumMath.equalsEpsilon = function(left, right, epsilon) { + epsilon = defaultValue(epsilon, 0.0); + return Math.abs(left - right) <= epsilon; + }; + + var factorials = [1]; + + /** + * Computes the factorial of the provided number. + * + * @memberof CesiumMath + * + * @param {Number} n The number whose factorial is to be computed. + * + * @returns {Number} The factorial of the provided number or undefined if the number is less than 0. + * + * @see <a href='http://en.wikipedia.org/wiki/Factorial'>Factorial on Wikipedia</a>. + * + * @example + * //Compute 7!, which is equal to 5040 + * var computedFactorial = CesiumMath.factorial(7); + * + * @exception {DeveloperError} A number greater than or equal to 0 is required. + */ + CesiumMath.factorial = function(n) { + if (typeof n !== 'number' || n < 0) { + throw new DeveloperError('A number greater than or equal to 0 is required.'); + } + + var length = factorials.length; + if (n >= length) { + var sum = factorials[length - 1]; + for ( var i = length; i <= n; i++) { + factorials.push(sum * i); + } + } + return factorials[n]; + }; + + /** + * Increments a number with a wrapping to a minimum value if the number exceeds the maximum value. + * + * @memberof CesiumMath + * + * @param {Number} [n] The number to be incremented. + * @param {Number} [maximumValue] The maximum incremented value before rolling over to the minimum value. + * @param {Number} [minimumValue=0.0] The number reset to after the maximum value has been exceeded. + * + * @returns {Number} The incremented number. + * + * @example + * var n = CesiumMath.incrementWrap(5, 10, 0); // returns 6 + * var n = CesiumMath.incrementWrap(10, 10, 0); // returns 0 + * + * @exception {DeveloperError} Maximum value must be greater than minimum value. + */ + CesiumMath.incrementWrap = function(n, maximumValue, minimumValue) { + minimumValue = defaultValue(minimumValue, 0.0); + + if (maximumValue <= minimumValue) { + throw new DeveloperError('Maximum value must be greater than minimum value.'); + } + + ++n; + if (n > maximumValue) { + n = minimumValue; + } + return n; + }; + + /** + * Determines if a positive integer is a power of two. + * + * @memberof CesiumMath + * + * @param {Number} n The positive integer to test. + * + * @returns {Boolean} <code>true</code> if the number if a power of two; otherwise, <code>false</code>. + * + * @exception {DeveloperError} A number greater than or equal to 0 is required. + * + * @example + * var t = CesiumMath.isPowerOfTwo(16); // true + * var f = CesiumMath.isPowerOfTwo(20); // false + */ + CesiumMath.isPowerOfTwo = function(n) { + if (typeof n !== 'number' || n < 0) { + throw new DeveloperError('A number greater than or equal to 0 is required.'); + } + + return (n !== 0) && ((n & (n - 1)) === 0); + }; + + /** + * Computes the next power-of-two integer greater than or equal to the provided positive integer. + * + * @memberof CesiumMath + * + * @param {Number} n The positive integer to test. + * + * @returns {Number} The next power-of-two integer. + * + * @exception {DeveloperError} A number greater than or equal to 0 is required. + * + * @example + * var n = CesiumMath.nextPowerOfTwo(29); // 32 + * var m = CesiumMath.nextPowerOfTwo(32); // 32 + */ + CesiumMath.nextPowerOfTwo = function(n) { + if (typeof n !== 'number' || n < 0) { + throw new DeveloperError('A number greater than or equal to 0 is required.'); + } + + // From http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 + --n; + n |= n >> 1; + n |= n >> 2; + n |= n >> 4; + n |= n >> 8; + n |= n >> 16; + ++n; + + return n; + }; + + /** + * Constraint a value to lie between two values. + * + * @memberof CesiumMath + * + * @param {Number} value The value to constrain. + * @param {Number} min The minimum value. + * @param {Number} max The maximum value. + * @returns The value clamped so that min <= value <= max. + */ + CesiumMath.clamp = function(value, min, max) { + return value < min ? min : value > max ? max : value; + }; + + var randomNumberGenerator = new MersenneTwister(); + + /** + * Sets the seed used by the random number generator + * in {@link CesiumMath#nextRandomNumber}. + * + * @memberof CesiumMath + * + * @param {Number} seed An integer used as the seed. + * + * @exception {DeveloperError} seed is required. + */ + CesiumMath.setRandomNumberSeed = function(seed) { + if (!defined(seed)) { + throw new DeveloperError('seed is required.'); + } + + randomNumberGenerator = new MersenneTwister(seed); + }; + + /** + * Generates a random number in the range of [0.0, 1.0) + * using a Mersenne twister. + * + * @memberof CesiumMath + * + * @returns A random number in the range of [0.0, 1.0). + * + * @see CesiumMath#setRandomNumberSeed + * @see http://en.wikipedia.org/wiki/Mersenne_twister + */ + CesiumMath.nextRandomNumber = function() { + return randomNumberGenerator.random(); + }; + + return CesiumMath; +}); + +/*global define*/ +define('Core/Cartographic',[ + './defaultValue', + './defined', + './DeveloperError', + './freezeObject', + './Math' + ], function( + defaultValue, + defined, + DeveloperError, + freezeObject, + CesiumMath) { + "use strict"; + + /** + * A position defined by longitude, latitude, and height. + * @alias Cartographic + * @constructor + * + * @param {Number} [longitude=0.0] The longitude, in radians. + * @param {Number} [latitude=0.0] The latitude, in radians. + * @param {Number} [height=0.0] The height, in meters, above the ellipsoid. + * + * @see Ellipsoid + */ + var Cartographic = function(longitude, latitude, height) { + /** + * The longitude, in radians. + * @type {Number} + * @default 0.0 + */ + this.longitude = defaultValue(longitude, 0.0); + + /** + * The latitude, in radians. + * @type {Number} + * @default 0.0 + */ + this.latitude = defaultValue(latitude, 0.0); + + /** + * The height, in meters, above the ellipsoid. + * @type {Number} + * @default 0.0 + */ + this.height = defaultValue(height, 0.0); + }; + + /** + * Creates a new Cartographic instance from longitude and latitude + * specified in degrees. The values in the resulting object will + * be in radians. + * @memberof Cartographic + * + * @param {Number} [longitude=0.0] The longitude, in degrees. + * @param {Number} [latitude=0.0] The latitude, in degrees. + * @param {Number} [height=0.0] The height, in meters, above the ellipsoid. + * @param {Cartographic} [result] The object onto which to store the result. + * @returns {Cartographic} The modified result parameter or a new Cartographic instance if one was not provided. + */ + Cartographic.fromDegrees = function(longitude, latitude, height, result) { + longitude = CesiumMath.toRadians(defaultValue(longitude, 0.0)); + latitude = CesiumMath.toRadians(defaultValue(latitude, 0.0)); + height = defaultValue(height, 0.0); + + if (!defined(result)) { + return new Cartographic(longitude, latitude, height); + } + + result.longitude = longitude; + result.latitude = latitude; + result.height = height; + return result; + }; + + /** + * Duplicates a Cartographic instance. + * @memberof Cartographic + * + * @param {Cartographic} cartographic The cartographic to duplicate. + * @param {Cartographic} [result] The object onto which to store the result. + * @returns {Cartographic} The modified result parameter or a new Cartographic instance if one was not provided. (Returns undefined if cartographic is undefined) + */ + Cartographic.clone = function(cartographic, result) { + if (!defined(cartographic)) { + return undefined; + } + if (!defined(result)) { + return new Cartographic(cartographic.longitude, cartographic.latitude, cartographic.height); + } + result.longitude = cartographic.longitude; + result.latitude = cartographic.latitude; + result.height = cartographic.height; + return result; + }; + + /** + * Compares the provided cartographics componentwise and returns + * <code>true</code> if they are equal, <code>false</code> otherwise. + * @memberof Cartographic + * + * @param {Cartographic} [left] The first cartographic. + * @param {Cartographic} [right] The second cartographic. + * @returns {Boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise. + */ + Cartographic.equals = function(left, right) { + return (left === right) || + ((defined(left)) && + (defined(right)) && + (left.longitude === right.longitude) && + (left.latitude === right.latitude) && + (left.height === right.height)); + }; + + /** + * Compares the provided cartographics componentwise and returns + * <code>true</code> if they are within the provided epsilon, + * <code>false</code> otherwise. + * @memberof Cartographic + * + * @param {Cartographic} [left] The first cartographic. + * @param {Cartographic} [right] The second cartographic. + * @param {Number} epsilon The epsilon to use for equality testing. + * @returns {Boolean} <code>true</code> if left and right are within the provided epsilon, <code>false</code> otherwise. + * + * @exception {DeveloperError} epsilon is required and must be a number. + */ + Cartographic.equalsEpsilon = function(left, right, epsilon) { + if (typeof epsilon !== 'number') { + throw new DeveloperError('epsilon is required and must be a number.'); + } + return (left === right) || + ((defined(left)) && + (defined(right)) && + (Math.abs(left.longitude - right.longitude) <= epsilon) && + (Math.abs(left.latitude - right.latitude) <= epsilon) && + (Math.abs(left.height - right.height) <= epsilon)); + }; + + /** + * Creates a string representing the provided cartographic in the format '(longitude, latitude, height)'. + * @memberof Cartographic + * + * @param {Cartographic} cartographic The cartographic to stringify. + * @returns {String} A string representing the provided cartographic in the format '(longitude, latitude, height)'. + * + * @exception {DeveloperError} cartographic is required. + */ + Cartographic.toString = function(cartographic) { + if (!defined(cartographic)) { + throw new DeveloperError('cartographic is required'); + } + return '(' + cartographic.longitude + ', ' + cartographic.latitude + ', ' + cartographic.height + ')'; + }; + + /** + * An immutable Cartographic instance initialized to (0.0, 0.0, 0.0). + * + * @memberof Cartographic + */ + Cartographic.ZERO = freezeObject(new Cartographic(0.0, 0.0, 0.0)); + + /** + * Duplicates this instance. + * @memberof Cartographic + * + * @param {Cartographic} [result] The object onto which to store the result. + * @returns {Cartographic} The modified result parameter or a new Cartographic instance if one was not provided. + */ + Cartographic.prototype.clone = function(result) { + return Cartographic.clone(this, result); + }; + + /** + * Compares the provided against this cartographic componentwise and returns + * <code>true</code> if they are equal, <code>false</code> otherwise. + * @memberof Cartographic + * + * @param {Cartographic} [right] The second cartographic. + * @returns {Boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise. + */ + Cartographic.prototype.equals = function(right) { + return Cartographic.equals(this, right); + }; + + /** + * Compares the provided against this cartographic componentwise and returns + * <code>true</code> if they are within the provided epsilon, + * <code>false</code> otherwise. + * @memberof Cartographic + * + * @param {Cartographic} [right] The second cartographic. + * @param {Number} epsilon The epsilon to use for equality testing. + * @returns {Boolean} <code>true</code> if left and right are within the provided epsilon, <code>false</code> otherwise. + * + * @exception {DeveloperError} epsilon is required and must be a number. + */ + Cartographic.prototype.equalsEpsilon = function(right, epsilon) { + return Cartographic.equalsEpsilon(this, right, epsilon); + }; + + /** + * Creates a string representing this cartographic in the format '(longitude, latitude, height)'. + * @memberof Cartographic + * + * @returns {String} A string representing the provided cartographic in the format '(longitude, latitude, height)'. + */ + Cartographic.prototype.toString = function() { + return Cartographic.toString(this); + }; + + return Cartographic; +}); + +/*global define*/ +define('Core/Ellipsoid',[ + './freezeObject', + './defaultValue', + './defined', + './DeveloperError', + './Math', + './Cartesian3', + './Cartographic' + ], function( + freezeObject, + defaultValue, + defined, + DeveloperError, + CesiumMath, + Cartesian3, + Cartographic) { + "use strict"; + + /** + * A quadratic surface defined in Cartesian coordinates by the equation + * <code>(x / a)^2 + (y / b)^2 + (z / c)^2 = 1</code>. Primarily used + * by Cesium to represent the shape of planetary bodies. + * + * Rather than constructing this object directly, one of the provided + * constants is normally used. + * @alias Ellipsoid + * @constructor + * @immutable + * + * @param {Number} [x=0] The radius in the x direction. + * @param {Number} [y=0] The radius in the y direction. + * @param {Number} [z=0] The radius in the z direction. + * + * @exception {DeveloperError} All radii components must be greater than or equal to zero. + * + * @see Ellipsoid.fromCartesian3 + * @see Ellipsoid.WGS84 + * @see Ellipsoid.UNIT_SPHERE + */ + var Ellipsoid = function(x, y, z) { + x = defaultValue(x, 0.0); + y = defaultValue(y, 0.0); + z = defaultValue(z, 0.0); + + if (x < 0.0 || y < 0.0 || z < 0.0) { + throw new DeveloperError('All radii components must be greater than or equal to zero.'); + } + + this._radii = new Cartesian3(x, y, z); + + this._radiiSquared = new Cartesian3(x * x, + y * y, + z * z); + + this._radiiToTheFourth = new Cartesian3(x * x * x * x, + y * y * y * y, + z * z * z * z); + + this._oneOverRadii = new Cartesian3(x === 0.0 ? 0.0 : 1.0 / x, + y === 0.0 ? 0.0 : 1.0 / y, + z === 0.0 ? 0.0 : 1.0 / z); + + this._oneOverRadiiSquared = new Cartesian3(x === 0.0 ? 0.0 : 1.0 / (x * x), + y === 0.0 ? 0.0 : 1.0 / (y * y), + z === 0.0 ? 0.0 : 1.0 / (z * z)); + + this._minimumRadius = Math.min(x, y, z); + + this._maximumRadius = Math.max(x, y, z); + + this._centerToleranceSquared = CesiumMath.EPSILON1; + }; + + /** + * Duplicates an Ellipsoid instance. + * + * @memberof Ellipsoid + * + * @param {Ellipsoid} ellipsoid The ellipsoid to duplicate. + * @param {Ellipsoid} [result] The object onto which to store the result, or undefined if a new + * instance should be created. + * @returns {Ellipsoid} The cloned Ellipsoid. (Returns undefined if ellipsoid is undefined) + */ + Ellipsoid.clone = function(ellipsoid, result) { + if (!defined(ellipsoid)) { + return undefined; + } + var radii = ellipsoid._radii; + + if (!defined(result)) { + return new Ellipsoid(radii.x, radii.y, radii.z); + } + + Cartesian3.clone(radii, result._radii); + Cartesian3.clone(ellipsoid._radiiSquared, result._radiiSquared); + Cartesian3.clone(ellipsoid._radiiToTheFourth, result._radiiToTheFourth); + Cartesian3.clone(ellipsoid._oneOverRadii, result._oneOverRadii); + Cartesian3.clone(ellipsoid._oneOverRadiiSquared, result._oneOverRadiiSquared); + result._minimumRadius = ellipsoid._minimumRadius; + result._maximumRadius = ellipsoid._maximumRadius; + result._centerToleranceSquared = ellipsoid._centerToleranceSquared; + + return result; + }; + + /** + * Computes an Ellipsoid from a Cartesian specifying the radii in x, y, and z directions. + * + * @param {Cartesian3} [radii=Cartesian3.ZERO] The ellipsoid's radius in the x, y, and z directions. + * @returns {Ellipsoid} A new Ellipsoid instance. + * + * @exception {DeveloperError} All radii components must be greater than or equal to zero. + * + * @see Ellipsoid.WGS84 + * @see Ellipsoid.UNIT_SPHERE + */ + Ellipsoid.fromCartesian3 = function(cartesian) { + if (!defined(cartesian)) { + return new Ellipsoid(); + } + return new Ellipsoid(cartesian.x, cartesian.y, cartesian.z); + }; + + /** + * An Ellipsoid instance initialized to the WGS84 standard. + * @memberof Ellipsoid + * + * @see czm_getWgs84EllipsoidEC + */ + Ellipsoid.WGS84 = freezeObject(new Ellipsoid(6378137.0, 6378137.0, 6356752.3142451793)); + + /** + * An Ellipsoid instance initialized to radii of (1.0, 1.0, 1.0). + * @memberof Ellipsoid + */ + Ellipsoid.UNIT_SPHERE = freezeObject(new Ellipsoid(1.0, 1.0, 1.0)); + + /** + * An Ellipsoid instance initialized to a sphere with the lunar radius. + * @memberof Ellipsoid + */ + Ellipsoid.MOON = freezeObject(new Ellipsoid(CesiumMath.LUNAR_RADIUS, CesiumMath.LUNAR_RADIUS, CesiumMath.LUNAR_RADIUS)); + + /** + * @memberof Ellipsoid + * @returns {Cartesian3} The radii of the ellipsoid. + */ + Ellipsoid.prototype.getRadii = function() { + return this._radii; + }; + + /** + * @memberof Ellipsoid + * @returns {Cartesian3} The squared radii of the ellipsoid. + */ + Ellipsoid.prototype.getRadiiSquared = function() { + return this._radiiSquared; + }; + + /** + * @memberof Ellipsoid + * @returns {Cartesian3} The radii of the ellipsoid raised to the fourth power. + */ + Ellipsoid.prototype.getRadiiToTheFourth = function() { + return this._radiiToTheFourth; + }; + + /** + * @memberof Ellipsoid + * @returns {Cartesian3} One over the radii of the ellipsoid. + */ + Ellipsoid.prototype.getOneOverRadii = function() { + return this._oneOverRadii; + }; + + /** + * @memberof Ellipsoid + * @returns {Cartesian3} One over the squared radii of the ellipsoid. + */ + Ellipsoid.prototype.getOneOverRadiiSquared = function() { + return this._oneOverRadiiSquared; + }; + + /** + * @memberof Ellipsoid + * @returns {Cartesian3} The minimum radius of the ellipsoid. + */ + Ellipsoid.prototype.getMinimumRadius = function() { + return this._minimumRadius; + }; + + /** + * @memberof Ellipsoid + * @returns {Cartesian3} The maximum radius of the ellipsoid. + */ + Ellipsoid.prototype.getMaximumRadius = function() { + return this._maximumRadius; + }; + + /** + * Duplicates an Ellipsoid instance. + * + * @memberof Ellipsoid + * + * @param {Ellipsoid} [result] The object onto which to store the result, or undefined if a new + * instance should be created. + * @returns {Ellipsoid} The cloned Ellipsoid. + */ + Ellipsoid.prototype.clone = function(result) { + return Ellipsoid.clone(this, result); + }; + + /** + * Computes the unit vector directed from the center of this ellipsoid toward the provided Cartesian position. + * @memberof Ellipsoid + * + * @param {Cartesian3} cartesian The Cartesian for which to to determine the geocentric normal. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if none was provided. + * + * @exception {DeveloperError} cartesian is required. + */ + Ellipsoid.prototype.geocentricSurfaceNormal = Cartesian3.normalize; + + /** + * Computes the normal of the plane tangent to the surface of the ellipsoid at the provided position. + * @memberof Ellipsoid + * + * @param {Cartographic} cartographic The cartographic position for which to to determine the geodetic normal. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if none was provided. + * + * @exception {DeveloperError} cartographic is required. + */ + Ellipsoid.prototype.geodeticSurfaceNormalCartographic = function(cartographic, result) { + if (!defined(cartographic)) { + throw new DeveloperError('cartographic is required.'); + } + + var longitude = cartographic.longitude; + var latitude = cartographic.latitude; + var cosLatitude = Math.cos(latitude); + + var x = cosLatitude * Math.cos(longitude); + var y = cosLatitude * Math.sin(longitude); + var z = Math.sin(latitude); + + if (!defined(result)) { + result = new Cartesian3(); + } + result.x = x; + result.y = y; + result.z = z; + return Cartesian3.normalize(result, result); + }; + + /** + * Computes the normal of the plane tangent to the surface of the ellipsoid at the provided position. + * @memberof Ellipsoid + * + * @param {Cartesian3} cartesian The Cartesian position for which to to determine the surface normal. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if none was provided. + * + * @exception {DeveloperError} cartesian is required. + */ + Ellipsoid.prototype.geodeticSurfaceNormal = function(cartesian, result) { + result = Cartesian3.multiplyComponents(cartesian, this._oneOverRadiiSquared, result); + return Cartesian3.normalize(result, result); + }; + + var cartographicToCartesianNormal = new Cartesian3(); + var cartographicToCartesianK = new Cartesian3(); + + /** + * Converts the provided cartographic to Cartesian representation. + * @memberof Ellipsoid + * + * @param {Cartographic} cartographic The cartographic position. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if none was provided. + * + * @exception {DeveloperError} cartographic is required. + * + * @example + * //Create a Cartographic and determine it's Cartesian representation on a WGS84 ellipsoid. + * var position = new Cartographic(Math.toRadians(21), Math.toRadians(78), 5000); + * var cartesianPosition = Ellipsoid.WGS84.cartographicToCartesian(position); + */ + Ellipsoid.prototype.cartographicToCartesian = function(cartographic, result) { + //`cartographic is required` is thrown from geodeticSurfaceNormalCartographic. + var n = cartographicToCartesianNormal; + var k = cartographicToCartesianK; + this.geodeticSurfaceNormalCartographic(cartographic, n); + Cartesian3.multiplyComponents(this._radiiSquared, n, k); + var gamma = Math.sqrt(Cartesian3.dot(n, k)); + Cartesian3.divideByScalar(k, gamma, k); + Cartesian3.multiplyByScalar(n, cartographic.height, n); + return Cartesian3.add(k, n, result); + }; + + /** + * Converts the provided array of cartographics to an array of Cartesians. + * @memberof Ellipsoid + * + * @param {Array} cartographics An array of cartographic positions. + * @param {Array} [result] The object onto which to store the result. + * @returns {Array} The modified result parameter or a new Array instance if none was provided. + * + * @exception {DeveloperError} cartographics is required. + * + * @example + * //Convert an array of Cartographics and determine their Cartesian representation on a WGS84 ellipsoid. + * var positions = [new Cartographic(Math.toRadians(21), Math.toRadians(78), 0), + * new Cartographic(Math.toRadians(21.321), Math.toRadians(78.123), 100), + * new Cartographic(Math.toRadians(21.645), Math.toRadians(78.456), 250) + * var cartesianPositions = Ellipsoid.WGS84.cartographicArrayToCartesianArray(positions); + */ + Ellipsoid.prototype.cartographicArrayToCartesianArray = function(cartographics, result) { + if (!defined(cartographics)) { + throw new DeveloperError('cartographics is required.'); + } + + var length = cartographics.length; + if (!defined(result)) { + result = new Array(length); + } else { + result.length = length; + } + for ( var i = 0; i < length; i++) { + result[i] = this.cartographicToCartesian(cartographics[i], result[i]); + } + return result; + }; + + var cartesianToCartographicN = new Cartesian3(); + var cartesianToCartographicP = new Cartesian3(); + var cartesianToCartographicH = new Cartesian3(); + + /** + * Converts the provided cartesian to cartographic representation. + * The cartesian is undefined at the center of the ellipsoid. + * @memberof Ellipsoid + * + * @param {Cartesian3} cartesian The Cartesian position to convert to cartographic representation. + * @param {Cartographic} [result] The object onto which to store the result. + * @returns {Cartographic} The modified result parameter, new Cartographic instance if none was provided, or undefined if the cartesian is at the center of the ellipsoid. + * + * @exception {DeveloperError} cartesian is required. + * + * @example + * //Create a Cartesian and determine it's Cartographic representation on a WGS84 ellipsoid. + * var position = new Cartesian(17832.12, 83234.52, 952313.73); + * var cartographicPosition = Ellipsoid.WGS84.cartesianToCartographic(position); + */ + Ellipsoid.prototype.cartesianToCartographic = function(cartesian, result) { + //`cartesian is required.` is thrown from scaleToGeodeticSurface + var p = this.scaleToGeodeticSurface(cartesian, cartesianToCartographicP); + + if (!defined(p)) { + return undefined; + } + + var n = this.geodeticSurfaceNormal(p, cartesianToCartographicN); + var h = Cartesian3.subtract(cartesian, p, cartesianToCartographicH); + + var longitude = Math.atan2(n.y, n.x); + var latitude = Math.asin(n.z); + var height = CesiumMath.sign(Cartesian3.dot(h, cartesian)) * Cartesian3.magnitude(h); + + if (!defined(result)) { + return new Cartographic(longitude, latitude, height); + } + result.longitude = longitude; + result.latitude = latitude; + result.height = height; + return result; + }; + + /** + * Converts the provided array of cartesians to an array of cartographics. + * @memberof Ellipsoid + * + * @param {Array} cartesians An array of Cartesian positions. + * @param {Array} [result] The object onto which to store the result. + * @returns {Array} The modified result parameter or a new Array instance if none was provided. + * + * @exception {DeveloperError} cartesians is required. + * + * @example + * //Create an array of Cartesians and determine their Cartographic representation on a WGS84 ellipsoid. + * var positions = [new Cartesian(17832.12, 83234.52, 952313.73), + * new Cartesian(17832.13, 83234.53, 952313.73), + * new Cartesian(17832.14, 83234.54, 952313.73)] + * var cartographicPositions = Ellipsoid.WGS84.cartesianArrayToCartographicArray(positions); + */ + Ellipsoid.prototype.cartesianArrayToCartographicArray = function(cartesians, result) { + if (!defined(cartesians)) { + throw new DeveloperError('cartesians is required.'); + } + + var length = cartesians.length; + if (!defined(result)) { + result = new Array(length); + } else { + result.length = length; + } + for ( var i = 0; i < length; ++i) { + result[i] = this.cartesianToCartographic(cartesians[i], result[i]); + } + return result; + }; + + var scaleToGeodeticSurfaceIntersection = new Cartesian3(); + var scaleToGeodeticSurfaceGradient = new Cartesian3(); + + /** + * Scales the provided Cartesian position along the geodetic surface normal + * so that it is on the surface of this ellipsoid. If the position is + * at the center of the ellipsoid, this function returns undefined. + * @memberof Ellipsoid + * + * @param {Cartesian3} cartesian The Cartesian position to scale. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter, a new Cartesian3 instance if none was provided, or undefined if the position is at the center. + * + * @exception {DeveloperError} cartesian is required. + */ + Ellipsoid.prototype.scaleToGeodeticSurface = function(cartesian, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required.'); + } + + var positionX = cartesian.x; + var positionY = cartesian.y; + var positionZ = cartesian.z; + + var oneOverRadii = this._oneOverRadii; + var oneOverRadiiX = oneOverRadii.x; + var oneOverRadiiY = oneOverRadii.y; + var oneOverRadiiZ = oneOverRadii.z; + + var x2 = positionX * positionX * oneOverRadiiX * oneOverRadiiX; + var y2 = positionY * positionY * oneOverRadiiY * oneOverRadiiY; + var z2 = positionZ * positionZ * oneOverRadiiZ * oneOverRadiiZ; + + // Compute the squared ellipsoid norm. + var squaredNorm = x2 + y2 + z2; + var ratio = Math.sqrt(1.0 / squaredNorm); + + // As an initial approximation, assume that the radial intersection is the projection point. + var intersection = Cartesian3.multiplyByScalar(cartesian, ratio, scaleToGeodeticSurfaceIntersection); + + //* If the position is near the center, the iteration will not converge. + if (squaredNorm < this._centerToleranceSquared) { + return !isFinite(ratio) ? undefined : Cartesian3.clone(intersection, result); + } + + var oneOverRadiiSquared = this._oneOverRadiiSquared; + var oneOverRadiiSquaredX = oneOverRadiiSquared.x; + var oneOverRadiiSquaredY = oneOverRadiiSquared.y; + var oneOverRadiiSquaredZ = oneOverRadiiSquared.z; + + // Use the gradient at the intersection point in place of the true unit normal. + // The difference in magnitude will be absorbed in the multiplier. + var gradient = scaleToGeodeticSurfaceGradient; + gradient.x = intersection.x * oneOverRadiiSquaredX * 2.0; + gradient.y = intersection.y * oneOverRadiiSquaredY * 2.0; + gradient.z = intersection.z * oneOverRadiiSquaredZ * 2.0; + + // Compute the initial guess at the normal vector multiplier, lambda. + var lambda = (1.0 - ratio) * Cartesian3.magnitude(cartesian) / (0.5 * Cartesian3.magnitude(gradient)); + var correction = 0.0; + + var func; + var denominator; + var xMultiplier; + var yMultiplier; + var zMultiplier; + var xMultiplier2; + var yMultiplier2; + var zMultiplier2; + var xMultiplier3; + var yMultiplier3; + var zMultiplier3; + + do { + lambda -= correction; + + xMultiplier = 1.0 / (1.0 + lambda * oneOverRadiiSquaredX); + yMultiplier = 1.0 / (1.0 + lambda * oneOverRadiiSquaredY); + zMultiplier = 1.0 / (1.0 + lambda * oneOverRadiiSquaredZ); + + xMultiplier2 = xMultiplier * xMultiplier; + yMultiplier2 = yMultiplier * yMultiplier; + zMultiplier2 = zMultiplier * zMultiplier; + + xMultiplier3 = xMultiplier2 * xMultiplier; + yMultiplier3 = yMultiplier2 * yMultiplier; + zMultiplier3 = zMultiplier2 * zMultiplier; + + func = x2 * xMultiplier2 + y2 * yMultiplier2 + z2 * zMultiplier2 - 1.0; + + // "denominator" here refers to the use of this expression in the velocity and acceleration + // computations in the sections to follow. + denominator = x2 * xMultiplier3 * oneOverRadiiSquaredX + y2 * yMultiplier3 * oneOverRadiiSquaredY + z2 * zMultiplier3 * oneOverRadiiSquaredZ; + + var derivative = -2.0 * denominator; + + correction = func / derivative; + } while (Math.abs(func) > CesiumMath.EPSILON12); + + if (!defined(result)) { + return new Cartesian3(positionX * xMultiplier, positionY * yMultiplier, positionZ * zMultiplier); + } + result.x = positionX * xMultiplier; + result.y = positionY * yMultiplier; + result.z = positionZ * zMultiplier; + return result; + }; + + /** + * Scales the provided Cartesian position along the geocentric surface normal + * so that it is on the surface of this ellipsoid. + * @memberof Ellipsoid + * + * @param {Cartesian3} cartesian The Cartesian position to scale. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if none was provided. + * + * @exception {DeveloperError} cartesian is required. + */ + Ellipsoid.prototype.scaleToGeocentricSurface = function(cartesian, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required.'); + } + + var positionX = cartesian.x; + var positionY = cartesian.y; + var positionZ = cartesian.z; + var oneOverRadiiSquared = this._oneOverRadiiSquared; + + var beta = 1.0 / Math.sqrt((positionX * positionX) * oneOverRadiiSquared.x + + (positionY * positionY) * oneOverRadiiSquared.y + + (positionZ * positionZ) * oneOverRadiiSquared.z); + + return Cartesian3.multiplyByScalar(cartesian, beta, result); + }; + + /** + * Transforms a Cartesian X, Y, Z position to the ellipsoid-scaled space by multiplying + * its components by the result of {@link Ellipsoid#getOneOverRadii}. + * + * @memberof Ellipsoid + * + * @param {Cartesian3} position The position to transform. + * @param {Cartesian3} [result] The position to which to copy the result, or undefined to create and + * return a new instance. + * @returns {Cartesian3} The position expressed in the scaled space. The returned instance is the + * one passed as the result parameter if it is not undefined, or a new instance of it is. + */ + Ellipsoid.prototype.transformPositionToScaledSpace = function(position, result) { + return Cartesian3.multiplyComponents(position, this._oneOverRadii, result); + }; + + /** + * Transforms a Cartesian X, Y, Z position from the ellipsoid-scaled space by multiplying + * its components by the result of {@link Ellipsoid#getRadii}. + * + * @memberof Ellipsoid + * + * @param {Cartesian3} position The position to transform. + * @param {Cartesian3} [result] The position to which to copy the result, or undefined to create and + * return a new instance. + * @returns {Cartesian3} The position expressed in the unscaled space. The returned instance is the + * one passed as the result parameter if it is not undefined, or a new instance of it is. + */ + Ellipsoid.prototype.transformPositionFromScaledSpace = function(position, result) { + return Cartesian3.multiplyComponents(position, this._radii, result); + }; + + /** + * Compares this Ellipsoid against the provided Ellipsoid componentwise and returns + * <code>true</code> if they are equal, <code>false</code> otherwise. + * @memberof Ellipsoid + * + * @param {Ellipsoid} [right] The other Ellipsoid. + * @returns {Boolean} <code>true</code> if they are equal, <code>false</code> otherwise. + */ + Ellipsoid.prototype.equals = function(right) { + return (this === right) || + (defined(right) && + Cartesian3.equals(this._radii, right._radii)); + }; + + /** + * Creates a string representing this Ellipsoid in the format '(radii.x, radii.y, radii.z)'. + * @memberof Ellipsoid + * + * @returns {String} A string representing this ellipsoid in the format '(radii.x, radii.y, radii.z)'. + */ + Ellipsoid.prototype.toString = function() { + return this._radii.toString(); + }; + + return Ellipsoid; +}); + +/*global define*/ +define('Core/GeographicProjection',[ + './defaultValue', + './defined', + './Cartesian3', + './Cartographic', + './Ellipsoid' + ], function( + defaultValue, + defined, + Cartesian3, + Cartographic, + Ellipsoid) { + "use strict"; + + /** + * A simple map projection where longitude and latitude are linearly mapped to X and Y by multiplying + * them by the {@link Ellipsoid#getMaximumRadius}. This projection + * is commonly known as geographic, equirectangular, equidistant cylindrical, or plate carrée. It + * is also known as EPSG:4326. + * + * @alias GeographicProjection + * @constructor + * @immutable + * + * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid. + * + * @see WebMercatorProjection + */ + var GeographicProjection = function(ellipsoid) { + this._ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); + this._semimajorAxis = this._ellipsoid.getMaximumRadius(); + this._oneOverSemimajorAxis = 1.0 / this._semimajorAxis; + }; + + /** + * Gets the {@link Ellipsoid}. + * + * @memberof GeographicProjection + * + * @returns {Ellipsoid} The ellipsoid. + */ + GeographicProjection.prototype.getEllipsoid = function() { + return this._ellipsoid; + }; + + /** + * Projects a set of {@link Cartographic} coordinates, in radians, to map coordinates, in meters. + * X and Y are the longitude and latitude, respectively, multiplied by the maximum radius of the + * ellipsoid. Z is the unmodified height. + * + * @memberof GeographicProjection + * + * @param {Cartographic} cartographic The coordinates to project. + * @param {Cartesian3} [result] An instance into which to copy the result. If this parameter is + * undefined, a new instance is created and returned. + * @returns {Cartesian3} The projected coordinates. If the result parameter is not undefined, the + * coordinates are copied there and that instance is returned. Otherwise, a new instance is + * created and returned. + */ + GeographicProjection.prototype.project = function(cartographic, result) { + // Actually this is the special case of equidistant cylindrical called the plate carree + var semimajorAxis = this._semimajorAxis; + var x = cartographic.longitude * semimajorAxis; + var y = cartographic.latitude * semimajorAxis; + var z = cartographic.height; + + if (!defined(result)) { + return new Cartesian3(x, y, z); + } + + result.x = x; + result.y = y; + result.z = z; + return result; + }; + + /** + * Unprojects a set of projected {@link Cartesian3} coordinates, in meters, to {@link Cartographic} + * coordinates, in radians. Longitude and Latitude are the X and Y coordinates, respectively, + * divided by the maximum radius of the ellipsoid. Height is the unmodified Z coordinate. + * + * @memberof GeographicProjection + * + * @param {Cartesian3} cartesian The coordinate to unproject. + * @param {Cartographic} [result] An instance into which to copy the result. If this parameter is + * undefined, a new instance is created and returned. + * @returns {Cartographic} The unprojected coordinates. If the result parameter is not undefined, the + * coordinates are copied there and that instance is returned. Otherwise, a new instance is + * created and returned. + */ + GeographicProjection.prototype.unproject = function(cartesian, result) { + var oneOverEarthSemimajorAxis = this._oneOverSemimajorAxis; + var longitude = cartesian.x * oneOverEarthSemimajorAxis; + var latitude = cartesian.y * oneOverEarthSemimajorAxis; + var height = cartesian.z; + + if (!defined(result)) { + return new Cartographic(longitude, latitude, height); + } + + result.longitude = longitude; + result.latitude = latitude; + result.height = height; + return result; + }; + + return GeographicProjection; +}); + +/*global define*/ +define('Core/Enumeration',['./defined'], function(defined) { + "use strict"; + + /** + * Constructs an enumeration that contains both a numeric value and a name. + * This is used so the name of the enumeration is available in the debugger. + * + * @param {Number} [value=undefined] The numeric value of the enumeration. + * @param {String} [name=undefined] The name of the enumeration for debugging purposes. + * @param {Object} [properties=undefined] An object containing extra properties to be added to the enumeration. + * + * @alias Enumeration + * @constructor + * @example + * // Create an object with two enumerations. + * var filter = { + * NEAREST : new Enumeration(0x2600, 'NEAREST'), + * LINEAR : new Enumeration(0x2601, 'LINEAR') + * }; + */ + var Enumeration = function(value, name, properties) { + /** + * The numeric value of the enumeration. + * @type {Number} + * @default undefined + */ + this.value = value; + + /** + * The name of the enumeration for debugging purposes. + * @type {String} + * @default undefined + */ + this.name = name; + + if (defined(properties)) { + for ( var propertyName in properties) { + if (properties.hasOwnProperty(propertyName)) { + this[propertyName] = properties[propertyName]; + } + } + } + }; + + /** + * Returns the numeric value of the enumeration. + * + * @memberof Enumeration + * + * @returns {Number} The numeric value of the enumeration. + */ + Enumeration.prototype.valueOf = function() { + return this.value; + }; + + /** + * Returns the name of the enumeration for debugging purposes. + * + * @memberof Enumeration + * + * @returns {String} The name of the enumeration for debugging purposes. + */ + Enumeration.prototype.toString = function() { + return this.name; + }; + + return Enumeration; +}); + +/*global define*/ +define('Core/Intersect',['./Enumeration'], function(Enumeration) { + "use strict"; + + /** + * This enumerated type is used in determining where, relative to the frustum, an + * object is located. The object can either be fully contained within the frustum (INSIDE), + * partially inside the frustum and partially outside (INTERSECTING), or somwhere entirely + * outside of the frustum's 6 planes (OUTSIDE). + * + * @exports Intersect + */ + var Intersect = { + /** + * Represents that an object is not contained within the frustum. + * + * @type {Enumeration} + * @constant + * @default -1 + */ + OUTSIDE : new Enumeration(-1, 'OUTSIDE'), + + /** + * Represents that an object intersects one of the frustum's planes. + * + * @type {Enumeration} + * @constant + * @default 0 + */ + INTERSECTING : new Enumeration(0, 'INTERSECTING'), + + /** + * Represents that an object is fully within the frustum. + * + * @type {Enumeration} + * @constant + * @default 1 + */ + INSIDE : new Enumeration(1, 'INSIDE') + }; + + return Intersect; +}); + +/*global define*/ +define('Core/Interval',['./defaultValue'], function(defaultValue) { + "use strict"; + + /** + * Represents the closed interval [start, stop]. + * @alias Interval + * @constructor + * + * @param {Number} [start=0.0] The beginning of the interval. + * @param {Number} [stop=0.0] The end of the interval. + */ + var Interval = function(start, stop) { + /** + * The beginning of the interval. + * @type {Number} + * @default 0.0 + */ + this.start = defaultValue(start, 0.0); + /** + * The end of the interval. + * @type {Number} + * @default 0.0 + */ + this.stop = defaultValue(stop, 0.0); + }; + + return Interval; +}); + +/*global define*/ +define('Core/Matrix3',[ + './Cartesian3', + './defaultValue', + './defined', + './DeveloperError', + './freezeObject', + './Math' + ], function( + Cartesian3, + defaultValue, + defined, + DeveloperError, + freezeObject, + CesiumMath) { + "use strict"; + + /** + * A 3x3 matrix, indexable as a column-major order array. + * Constructor parameters are in row-major order for code readability. + * @alias Matrix3 + * @constructor + * + * @param {Number} [column0Row0=0.0] The value for column 0, row 0. + * @param {Number} [column1Row0=0.0] The value for column 1, row 0. + * @param {Number} [column2Row0=0.0] The value for column 2, row 0. + * @param {Number} [column0Row1=0.0] The value for column 0, row 1. + * @param {Number} [column1Row1=0.0] The value for column 1, row 1. + * @param {Number} [column2Row1=0.0] The value for column 2, row 1. + * @param {Number} [column0Row2=0.0] The value for column 0, row 2. + * @param {Number} [column1Row2=0.0] The value for column 1, row 2. + * @param {Number} [column2Row2=0.0] The value for column 2, row 2. + * + * @see Matrix3.fromColumnMajor + * @see Matrix3.fromRowMajorArray + * @see Matrix3.fromQuaternion + * @see Matrix3.fromScale + * @see Matrix3.fromUniformScale + * @see Matrix2 + * @see Matrix4 + */ + var Matrix3 = function(column0Row0, column1Row0, column2Row0, + column0Row1, column1Row1, column2Row1, + column0Row2, column1Row2, column2Row2) { + this[0] = defaultValue(column0Row0, 0.0); + this[1] = defaultValue(column0Row1, 0.0); + this[2] = defaultValue(column0Row2, 0.0); + this[3] = defaultValue(column1Row0, 0.0); + this[4] = defaultValue(column1Row1, 0.0); + this[5] = defaultValue(column1Row2, 0.0); + this[6] = defaultValue(column2Row0, 0.0); + this[7] = defaultValue(column2Row1, 0.0); + this[8] = defaultValue(column2Row2, 0.0); + }; + + /** + * Duplicates a Matrix3 instance. + * @memberof Matrix3 + * + * @param {Matrix3} matrix The matrix to duplicate. + * @param {Matrix3} [result] The object onto which to store the result. + * @returns {Matrix3} The modified result parameter or a new Matrix3 instance if one was not provided. (Returns undefined if matrix is undefined) + */ + Matrix3.clone = function(values, result) { + if (!defined(values)) { + return undefined; + } + if (!defined(result)) { + return new Matrix3(values[0], values[3], values[6], + values[1], values[4], values[7], + values[2], values[5], values[8]); + } + result[0] = values[0]; + result[1] = values[1]; + result[2] = values[2]; + result[3] = values[3]; + result[4] = values[4]; + result[5] = values[5]; + result[6] = values[6]; + result[7] = values[7]; + result[8] = values[8]; + return result; + }; + + /** + * Creates a Matrix3 from 9 consecutive elements in an array. + * @memberof Matrix3 + * + * @param {Array} array The array whose 9 consecutive elements correspond to the positions of the matrix. Assumes column-major order. + * @param {Number} [startingIndex=0] The offset into the array of the first element, which corresponds to first column first row position in the matrix. + * @param {Matrix3} [result] The object onto which to store the result. + * + * @returns {Matrix3} The modified result parameter or a new Matrix3 instance if one was not provided. + * + * @exception {DeveloperError} array is required. + * + * @example + * // Create the Matrix3: + * // [1.0, 2.0, 3.0] + * // [1.0, 2.0, 3.0] + * // [1.0, 2.0, 3.0] + * + * var v = [1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0]; + * var m = Matrix3.fromArray(v); + * + * // Create same Matrix3 with using an offset into an array + * var v2 = [0.0, 0.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0]; + * var m2 = Matrix3.fromArray(v2, 2); + */ + Matrix3.fromArray = function(array, startingIndex, result) { + if (!defined(array)) { + throw new DeveloperError('array is required'); + } + + startingIndex = defaultValue(startingIndex, 0); + + if (!defined(result)) { + result = new Matrix3(); + } + + result[0] = array[startingIndex]; + result[1] = array[startingIndex + 1]; + result[2] = array[startingIndex + 2]; + result[3] = array[startingIndex + 3]; + result[4] = array[startingIndex + 4]; + result[5] = array[startingIndex + 5]; + result[6] = array[startingIndex + 6]; + result[7] = array[startingIndex + 7]; + result[8] = array[startingIndex + 8]; + result[9] = array[startingIndex + 9]; + return result; + }; + + /** + * Creates a Matrix3 instance from a column-major order array. + * @memberof Matrix3 + * @function + * + * @param {Array} values The column-major order array. + * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix3 instance if one was not provided. + * + * @exception {DeveloperError} values is required. + */ + Matrix3.fromColumnMajorArray = function(values, result) { + if (!defined(values)) { + throw new DeveloperError('values parameter is required'); + } + + return Matrix3.clone(values, result); + }; + + /** + * Creates a Matrix3 instance from a row-major order array. + * The resulting matrix will be in column-major order. + * @memberof Matrix3 + * + * @param {Array} values The row-major order array. + * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix3 instance if one was not provided. + * + * @exception {DeveloperError} values is required. + */ + Matrix3.fromRowMajorArray = function(values, result) { + if (!defined(values)) { + throw new DeveloperError('values is required.'); + } + + if (!defined(result)) { + return new Matrix3(values[0], values[1], values[2], + values[3], values[4], values[5], + values[6], values[7], values[8]); + } + result[0] = values[0]; + result[1] = values[3]; + result[2] = values[6]; + result[3] = values[1]; + result[4] = values[4]; + result[5] = values[7]; + result[6] = values[2]; + result[7] = values[5]; + result[8] = values[8]; + return result; + }; + + /** + * Computes a 3x3 rotation matrix from the provided quaternion. + * @memberof Matrix3 + * + * @param {Quaternion} quaternion the quaternion to use. + * + * @returns {Matrix3} The 3x3 rotation matrix from this quaternion. + */ + Matrix3.fromQuaternion = function(quaternion, result) { + if (!defined(quaternion)) { + throw new DeveloperError('quaternion is required'); + } + + var x2 = quaternion.x * quaternion.x; + var xy = quaternion.x * quaternion.y; + var xz = quaternion.x * quaternion.z; + var xw = quaternion.x * quaternion.w; + var y2 = quaternion.y * quaternion.y; + var yz = quaternion.y * quaternion.z; + var yw = quaternion.y * quaternion.w; + var z2 = quaternion.z * quaternion.z; + var zw = quaternion.z * quaternion.w; + var w2 = quaternion.w * quaternion.w; + + var m00 = x2 - y2 - z2 + w2; + var m01 = 2.0 * (xy - zw); + var m02 = 2.0 * (xz + yw); + + var m10 = 2.0 * (xy + zw); + var m11 = -x2 + y2 - z2 + w2; + var m12 = 2.0 * (yz - xw); + + var m20 = 2.0 * (xz - yw); + var m21 = 2.0 * (yz + xw); + var m22 = -x2 - y2 + z2 + w2; + + 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. + * @memberof Matrix3 + * + * @param {Cartesian3} scale The x, y, and z scale factors. + * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix3 instance if one was not provided. + * + * @exception {DeveloperError} scale is required. + * + * @example + * // Creates + * // [7.0, 0.0, 0.0] + * // [0.0, 8.0, 0.0] + * // [0.0, 0.0, 9.0] + * var m = Matrix3.fromScale(new Cartesian3(7.0, 8.0, 9.0)); + */ + Matrix3.fromScale = function(scale, result) { + if (!defined(scale)) { + throw new DeveloperError('scale is required.'); + } + + if (!defined(result)) { + return new Matrix3( + scale.x, 0.0, 0.0, + 0.0, scale.y, 0.0, + 0.0, 0.0, scale.z); + } + + result[0] = scale.x; + result[1] = 0.0; + result[2] = 0.0; + result[3] = 0.0; + result[4] = scale.y; + result[5] = 0.0; + result[6] = 0.0; + result[7] = 0.0; + result[8] = scale.z; + return result; + }; + + /** + * Computes a Matrix3 instance representing a uniform scale. + * @memberof Matrix3 + * + * @param {Number} scale The uniform scale factor. + * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix3 instance if one was not provided. + * + * @exception {DeveloperError} scale is required. + * + * @example + * // Creates + * // [2.0, 0.0, 0.0] + * // [0.0, 2.0, 0.0] + * // [0.0, 0.0, 2.0] + * var m = Matrix3.fromUniformScale(2.0); + */ + Matrix3.fromUniformScale = function(scale, result) { + if (typeof scale !== 'number') { + throw new DeveloperError('scale is required.'); + } + + if (!defined(result)) { + return new Matrix3( + scale, 0.0, 0.0, + 0.0, scale, 0.0, + 0.0, 0.0, scale); + } + + result[0] = scale; + result[1] = 0.0; + result[2] = 0.0; + result[3] = 0.0; + result[4] = scale; + result[5] = 0.0; + result[6] = 0.0; + result[7] = 0.0; + result[8] = scale; + return result; + }; + + /** + * Creates a rotation matrix around the x-axis. + * + * @param {Number} angle The angle, in radians, of the rotation. Positive angles are counterclockwise. + * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created. + * + * @returns The modified result parameter, or a new Matrix3 instance if one was not provided. + * + * @exception {DeveloperError} angle is required. + * + * @example + * // Rotate a point 45 degrees counterclockwise around the x-axis. + * var p = new Cartesian3(5, 6, 7); + * var m = Matrix3.fromRotationX(CesiumMath.toRadians(45.0)); + * var rotated = Matrix3.multiplyByVector(m, p); + */ + Matrix3.fromRotationX = function(angle, result) { + if (!defined(angle)) { + throw new DeveloperError('angle is required.'); + } + + var cosAngle = Math.cos(angle); + var sinAngle = Math.sin(angle); + + if (!defined(result)) { + return new Matrix3( + 1.0, 0.0, 0.0, + 0.0, cosAngle, -sinAngle, + 0.0, sinAngle, cosAngle); + } + + result[0] = 1.0; + result[1] = 0.0; + result[2] = 0.0; + result[3] = 0.0; + result[4] = cosAngle; + result[5] = sinAngle; + result[6] = 0.0; + result[7] = -sinAngle; + result[8] = cosAngle; + + return result; + }; + + /** + * Creates a rotation matrix around the y-axis. + * + * @param {Number} angle The angle, in radians, of the rotation. Positive angles are counterclockwise. + * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created. + * + * @returns The modified result parameter, or a new Matrix3 instance if one was not provided. + * + * @exception {DeveloperError} angle is required. + * + * @example + * // Rotate a point 45 degrees counterclockwise around the y-axis. + * var p = new Cartesian3(5, 6, 7); + * var m = Matrix3.fromRotationY(CesiumMath.toRadians(45.0)); + * var rotated = Matrix3.multiplyByVector(m, p); + */ + Matrix3.fromRotationY = function(angle, result) { + if (!defined(angle)) { + throw new DeveloperError('angle is required.'); + } + + var cosAngle = Math.cos(angle); + var sinAngle = Math.sin(angle); + + if (!defined(result)) { + return new Matrix3( + cosAngle, 0.0, sinAngle, + 0.0, 1.0, 0.0, + -sinAngle, 0.0, cosAngle); + } + + result[0] = cosAngle; + result[1] = 0.0; + result[2] = -sinAngle; + result[3] = 0.0; + result[4] = 1.0; + result[5] = 0.0; + result[6] = sinAngle; + result[7] = 0.0; + result[8] = cosAngle; + + return result; + }; + + /** + * Creates a rotation matrix around the z-axis. + * + * @param {Number} angle The angle, in radians, of the rotation. Positive angles are counterclockwise. + * @param {Matrix3} [result] The object in which the result will be stored, if undefined a new instance will be created. + * + * @returns The modified result parameter, or a new Matrix3 instance if one was not provided. + * + * @exception {DeveloperError} angle is required. + * + * @example + * // Rotate a point 45 degrees counterclockwise around the z-axis. + * var p = new Cartesian3(5, 6, 7); + * var m = Matrix3.fromRotationZ(CesiumMath.toRadians(45.0)); + * var rotated = Matrix3.multiplyByVector(m, p); + */ + Matrix3.fromRotationZ = function(angle, result) { + if (!defined(angle)) { + throw new DeveloperError('angle is required.'); + } + + var cosAngle = Math.cos(angle); + var sinAngle = Math.sin(angle); + + if (!defined(result)) { + return new Matrix3( + cosAngle, -sinAngle, 0.0, + sinAngle, cosAngle, 0.0, + 0.0, 0.0, 1.0); + } + + result[0] = cosAngle; + result[1] = sinAngle; + result[2] = 0.0; + result[3] = -sinAngle; + result[4] = cosAngle; + result[5] = 0.0; + result[6] = 0.0; + result[7] = 0.0; + result[8] = 1.0; + + return result; + }; + + /** + * Creates an Array from the provided Matrix3 instance. + * The array will be in column-major order. + * @memberof Matrix3 + * + * @param {Matrix3} matrix The matrix to use.. + * @param {Array} [result] The Array onto which to store the result. + * @returns {Array} The modified Array parameter or a new Array instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + */ + Matrix3.toArray = function(matrix, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + + if (!defined(result)) { + return [matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], matrix[6], matrix[7], matrix[8]]; + } + result[0] = matrix[0]; + result[1] = matrix[1]; + result[2] = matrix[2]; + result[3] = matrix[3]; + result[4] = matrix[4]; + result[5] = matrix[5]; + result[6] = matrix[6]; + result[7] = matrix[7]; + result[8] = matrix[8]; + return result; + }; + + /** + * Computes the array index of the element at the provided row and column. + * @memberof Matrix3 + * + * @param {Number} row The zero-based index of the row. + * @param {Number} column The zero-based index of the column. + * @returns {Number} The index of the element at the provided row and column. + * + * @exception {DeveloperError} row is required and must be 0, 1, or 2. + * @exception {DeveloperError} column is required and must be 0, 1, or 2. + * + * @example + * var myMatrix = new Matrix3(); + * var column1Row0Index = Matrix3.getElementIndex(1, 0); + * var column1Row0 = myMatrix[column1Row0Index] + * myMatrix[column1Row0Index] = 10.0; + */ + Matrix3.getElementIndex = function(column, row) { + if (typeof row !== 'number' || row < 0 || row > 2) { + throw new DeveloperError('row is required and must be 0, 1, or 2.'); + } + if (typeof column !== 'number' || column < 0 || column > 2) { + throw new DeveloperError('column is required and must be 0, 1, or 2.'); + } + + return column * 3 + row; + }; + + /** + * Retrieves a copy of the matrix column at the provided index as a Cartesian3 instance. + * @memberof Matrix3 + * + * @param {Matrix3} matrix The matrix to use. + * @param {Number} index The zero-based index of the column to retrieve. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} index is required and must be 0, 1, or 2. + * + * @see Cartesian3 + */ + Matrix3.getColumn = function(matrix, index, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required.'); + } + + if (typeof index !== 'number' || index < 0 || index > 2) { + throw new DeveloperError('index is required and must be 0, 1, or 2.'); + } + + var startIndex = index * 3; + var x = matrix[startIndex]; + var y = matrix[startIndex + 1]; + var z = matrix[startIndex + 2]; + + if (!defined(result)) { + return new Cartesian3(x, y, z); + } + result.x = x; + result.y = y; + result.z = z; + return result; + }; + + /** + * Computes a new matrix that replaces the specified column in the provided matrix with the provided Cartesian3 instance. + * @memberof Matrix3 + * + * @param {Matrix3} matrix The matrix to use. + * @param {Number} index The zero-based index of the column to set. + * @param {Cartesian3} cartesian The Cartesian whose values will be assigned to the specified column. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Matrix3} The modified result parameter or a new Matrix3 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} cartesian is required. + * @exception {DeveloperError} index is required and must be 0, 1, or 2. + * + * @see Cartesian3 + */ + Matrix3.setColumn = function(matrix, index, cartesian, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + if (typeof index !== 'number' || index < 0 || index > 2) { + throw new DeveloperError('index is required and must be 0, 1, or 2.'); + } + + result = Matrix3.clone(matrix, result); + var startIndex = index * 3; + result[startIndex] = cartesian.x; + result[startIndex + 1] = cartesian.y; + result[startIndex + 2] = cartesian.z; + return result; + }; + + /** + * Retrieves a copy of the matrix row at the provided index as a Cartesian3 instance. + * @memberof Matrix3 + * + * @param {Matrix3} matrix The matrix to use. + * @param {Number} index The zero-based index of the row to retrieve. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} index is required and must be 0, 1, or 2. + * + * @see Cartesian3 + */ + Matrix3.getRow = function(matrix, index, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required.'); + } + + if (typeof index !== 'number' || index < 0 || index > 2) { + throw new DeveloperError('index is required and must be 0, 1, or 2.'); + } + + var x = matrix[index]; + var y = matrix[index + 3]; + var z = matrix[index + 6]; + + if (!defined(result)) { + return new Cartesian3(x, y, z); + } + result.x = x; + result.y = y; + result.z = z; + return result; + }; + + /** + * Computes a new matrix that replaces the specified row in the provided matrix with the provided Cartesian3 instance. + * @memberof Matrix3 + * + * @param {Matrix3} matrix The matrix to use. + * @param {Number} index The zero-based index of the row to set. + * @param {Cartesian3} cartesian The Cartesian whose values will be assigned to the specified row. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Matrix3} The modified result parameter or a new Matrix3 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} cartesian is required. + * @exception {DeveloperError} index is required and must be 0, 1, or 2. + * + * @see Cartesian3 + */ + Matrix3.setRow = function(matrix, index, cartesian, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + if (typeof index !== 'number' || index < 0 || index > 2) { + throw new DeveloperError('index is required and must be 0, 1, or 2.'); + } + + result = Matrix3.clone(matrix, result); + result[index] = cartesian.x; + result[index + 3] = cartesian.y; + result[index + 6] = cartesian.z; + return result; + }; + + /** + * Computes the product of two matrices. + * @memberof Matrix3 + * + * @param {Matrix3} left The first matrix. + * @param {Matrix3} right The second matrix. + * @param {Matrix3} [result] The object onto which to store the result. + * @returns {Matrix3} The modified result parameter or a new Matrix3 instance if one was not provided. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + */ + Matrix3.multiply = function(left, right, result) { + if (!defined(left)) { + throw new DeveloperError('left is required'); + } + if (!defined(right)) { + throw new DeveloperError('right is required'); + } + + var column0Row0 = left[0] * right[0] + left[3] * right[1] + left[6] * right[2]; + var column0Row1 = left[1] * right[0] + left[4] * right[1] + left[7] * right[2]; + var column0Row2 = left[2] * right[0] + left[5] * right[1] + left[8] * right[2]; + + var column1Row0 = left[0] * right[3] + left[3] * right[4] + left[6] * right[5]; + var column1Row1 = left[1] * right[3] + left[4] * right[4] + left[7] * right[5]; + var column1Row2 = left[2] * right[3] + left[5] * right[4] + left[8] * right[5]; + + var column2Row0 = left[0] * right[6] + left[3] * right[7] + left[6] * right[8]; + var column2Row1 = left[1] * right[6] + left[4] * right[7] + left[7] * right[8]; + var column2Row2 = left[2] * right[6] + left[5] * right[7] + left[8] * right[8]; + + if (!defined(result)) { + return new Matrix3(column0Row0, column1Row0, column2Row0, + column0Row1, column1Row1, column2Row1, + column0Row2, column1Row2, column2Row2); + } + result[0] = column0Row0; + result[1] = column0Row1; + result[2] = column0Row2; + result[3] = column1Row0; + result[4] = column1Row1; + result[5] = column1Row2; + result[6] = column2Row0; + result[7] = column2Row1; + result[8] = column2Row2; + return result; + }; + + /** + * Computes the product of a matrix and a column vector. + * @memberof Matrix3 + * + * @param {Matrix3} matrix The matrix. + * @param {Cartesian3} cartesian The column. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} cartesian is required. + */ + Matrix3.multiplyByVector = function(matrix, cartesian, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + var vX = cartesian.x; + var vY = cartesian.y; + var vZ = cartesian.z; + + var x = matrix[0] * vX + matrix[3] * vY + matrix[6] * vZ; + var y = matrix[1] * vX + matrix[4] * vY + matrix[7] * vZ; + var z = matrix[2] * vX + matrix[5] * vY + matrix[8] * vZ; + + if (!defined(result)) { + return new Cartesian3(x, y, z); + } + result.x = x; + result.y = y; + result.z = z; + return result; + }; + + /** + * Computes the product of a matrix and a scalar. + * @memberof Matrix3 + * + * @param {Matrix3} matrix The matrix. + * @param {Number} scalar The number to multiply by. + * @param {Matrix3} [result] The object onto which to store the result. + * @returns {Matrix3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} scalar is required and must be a number. + */ + Matrix3.multiplyByScalar = function(matrix, scalar, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + if (typeof scalar !== 'number') { + throw new DeveloperError('scalar is required and must be a number'); + } + + if (!defined(result)) { + return new Matrix3(matrix[0] * scalar, matrix[3] * scalar, matrix[6] * scalar, + matrix[1] * scalar, matrix[4] * scalar, matrix[7] * scalar, + matrix[2] * scalar, matrix[5] * scalar, matrix[8] * scalar); + } + result[0] = matrix[0] * scalar; + result[1] = matrix[1] * scalar; + result[2] = matrix[2] * scalar; + result[3] = matrix[3] * scalar; + result[4] = matrix[4] * scalar; + result[5] = matrix[5] * scalar; + result[6] = matrix[6] * scalar; + result[7] = matrix[7] * scalar; + result[8] = matrix[8] * scalar; + return result; + }; + + /** + * Creates a negated copy of the provided matrix. + * @memberof Matrix3 + * + * @param {Matrix3} matrix The matrix to negate. + * @param {Matrix3} [result] The object onto which to store the result. + * @returns {Matrix3} The modified result parameter or a new Matrix3 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + */ + Matrix3.negate = function(matrix, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + + if (!defined(result)) { + return new Matrix3(-matrix[0], -matrix[3], -matrix[6], + -matrix[1], -matrix[4], -matrix[7], + -matrix[2], -matrix[5], -matrix[8]); + } + result[0] = -matrix[0]; + result[1] = -matrix[1]; + result[2] = -matrix[2]; + result[3] = -matrix[3]; + result[4] = -matrix[4]; + result[5] = -matrix[5]; + result[6] = -matrix[6]; + result[7] = -matrix[7]; + result[8] = -matrix[8]; + return result; + }; + + /** + * Computes the transpose of the provided matrix. + * @memberof Matrix3 + * + * @param {Matrix3} matrix The matrix to transpose. + * @param {Matrix3} [result] The object onto which to store the result. + * @returns {Matrix3} The modified result parameter or a new Matrix3 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + */ + Matrix3.transpose = function(matrix, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + + var column0Row0 = matrix[0]; + var column0Row1 = matrix[3]; + var column0Row2 = matrix[6]; + var column1Row0 = matrix[1]; + var column1Row1 = matrix[4]; + var column1Row2 = matrix[7]; + var column2Row0 = matrix[2]; + var column2Row1 = matrix[5]; + var column2Row2 = matrix[8]; + + if (!defined(result)) { + return new Matrix3(column0Row0, column1Row0, column2Row0, + column0Row1, column1Row1, column2Row1, + column0Row2, column1Row2, column2Row2); + } + result[0] = column0Row0; + result[1] = column0Row1; + result[2] = column0Row2; + result[3] = column1Row0; + result[4] = column1Row1; + result[5] = column1Row2; + result[6] = column2Row0; + result[7] = column2Row1; + result[8] = column2Row2; + return result; + }; + + function computeFrobeniusNorm(matrix) { + var norm = 0.0; + for (var i = 0; i < 9; ++i) { + var temp = matrix[i]; + norm += temp * temp; + } + + return Math.sqrt(norm); + } + + var rowVal = [1, 0, 0]; + var colVal = [2, 2, 1]; + + function offDiagonalFrobeniusNorm(matrix) { + // Computes the "off-diagonal" Frobenius norm. + // Assumes matrix is symmetric. + + var norm = 0.0; + for (var i = 0; i < 3; ++i) { + var temp = matrix[Matrix3.getElementIndex(colVal[i], rowVal[i])]; + norm += 2.0 * temp * temp; + } + + return Math.sqrt(norm); + } + + function shurDecomposition(matrix, result) { + // This routine was created based upon Matrix Computations, 3rd ed., by Golub and Van Loan, + // section 8.4.2 The 2by2 Symmetric Schur Decomposition. + // + // The routine takes a matrix, which is assumed to be symmetric, and + // finds the largest off-diagonal term, and then creates + // a matrix (result) which can be used to help reduce it + + var tolerance = CesiumMath.EPSILON15; + + var maxDiagonal = 0.0; + var rotAxis = 1; + + // find pivot (rotAxis) based on max diagonal of matrix + for (var i = 0; i < 3; ++i) { + var temp = Math.abs(matrix[Matrix3.getElementIndex(colVal[i], rowVal[i])]); + if (temp > maxDiagonal) { + rotAxis = i; + maxDiagonal = temp; + } + } + + var c = 1.0; + var s = 0.0; + + var p = rowVal[rotAxis]; + var q = colVal[rotAxis]; + + if (Math.abs(matrix[Matrix3.getElementIndex(q, p)]) > tolerance) { + var qq = matrix[Matrix3.getElementIndex(q, q)]; + var pp = matrix[Matrix3.getElementIndex(p, p)]; + var qp = matrix[Matrix3.getElementIndex(q, p)]; + + var tau = (qq - pp) / 2.0 / qp; + var t; + + if (tau < 0.0) { + t = -1.0 / (-tau + Math.sqrt(1.0 + tau * tau)); + } else { + t = 1.0 / (tau + Math.sqrt(1.0 + tau * tau)); + } + + c = 1.0 / Math.sqrt(1.0 + t * t); + s = t * c; + } + + result = Matrix3.clone(Matrix3.IDENTITY, result); + + result[Matrix3.getElementIndex(p, p)] = result[Matrix3.getElementIndex(q, q)] = c; + result[Matrix3.getElementIndex(q, p)] = s; + result[Matrix3.getElementIndex(p, q)] = -s; + + return result; + } + + var jMatrix = new Matrix3(); + var jMatrixTranspose = new Matrix3(); + + /** + * Computes the eigenvectors and eigenvalues of a symmetric matrix. + * <p> + * Returns a diagonal matrix and unitary matrix such that: + * <code>matrix = unitary matrix * diagonal matrix * transpose(unitary matrix)</code> + * </p> + * <p> + * The values along the diagonal of the diagonal matrix are the eigenvalues. The columns + * of the unitary matrix are the corresponding eigenvectors. + * </p> + * @memberof Matrix3 + * + * @param {Matrix3} matrix The matrix to decompose into diagonal and unitary matrix. Expected to be symmetric. + * @param {Object} [result] An object with unitary and diagonal properties which are matrices onto which to store the result. + * @returns {Object} An object with unitary and diagonal properties which are the unitary and diagonal matrices, respectively. + * + * @example + * var a = //... symetric matrix + * var result = { + * unitary : new Matrix3(), + * diagonal : new Matrix3() + * }; + * Matrix3.getEigenDecomposition(a, result); + * + * var unitaryTranspose = Matrix3.transpose(result.unitary); + * var b = Matrix.multiply(result.unitary, result.diagonal); + * Matrix3.multiply(b, unitaryTranspose, b); // b is now equal to a + * + * var lambda = Matrix3.getColumn(result.diagonal, 0).x; // first eigenvalue + * var v = Matrix3.getColumn(result.unitary, 0); // first eigenvector + * var c = Cartesian3.multiplyByScalar(v, lambda); // equal to Matrix3.multiplyByVector(a, v) + */ + Matrix3.getEigenDecomposition = function(matrix, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required.'); + } + + // This routine was created based upon Matrix Computations, 3rd ed., by Golub and Van Loan, + // section 8.4.3 The Classical Jacobi Algorithm + + var tolerance = CesiumMath.EPSILON20; + var maxSweeps = 10; + + var count = 0; + var sweep = 0; + + if (!defined(result)) { + result = {}; + } + + var unitaryMatrix = result.unitary = Matrix3.clone(Matrix3.IDENTITY, result.unitary); + var diagMatrix = result.diagonal = Matrix3.clone(matrix, result.diagonal); + + var epsilon = tolerance * computeFrobeniusNorm(diagMatrix); + + while (sweep < maxSweeps && offDiagonalFrobeniusNorm(diagMatrix) > epsilon) { + shurDecomposition(diagMatrix, jMatrix); + Matrix3.transpose(jMatrix, jMatrixTranspose); + Matrix3.multiply(diagMatrix, jMatrix, diagMatrix); + Matrix3.multiply(jMatrixTranspose, diagMatrix, diagMatrix); + Matrix3.multiply(unitaryMatrix, jMatrix, unitaryMatrix); + + if (++count > 2) { + ++sweep; + count = 0; + } + } + + return result; + }; + + /** + * Computes a matrix, which contains the absolute (unsigned) values of the provided matrix's elements. + * @memberof Matrix3 + * + * @param {Matrix3} matrix The matrix with signed elements. + * @param {Matrix3} [result] The object onto which to store the result. + * @returns {Matrix3} The modified result parameter or a new Matrix3 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + */ + Matrix3.abs = function(matrix, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + + if (!defined(result)) { + return new Matrix3(Math.abs(matrix[0]), Math.abs(matrix[3]), Math.abs(matrix[6]), + Math.abs(matrix[1]), Math.abs(matrix[4]), Math.abs(matrix[7]), + Math.abs(matrix[2]), Math.abs(matrix[5]), Math.abs(matrix[8])); + } + result[0] = Math.abs(matrix[0]); + result[1] = Math.abs(matrix[1]); + result[2] = Math.abs(matrix[2]); + result[3] = Math.abs(matrix[3]); + result[4] = Math.abs(matrix[4]); + result[5] = Math.abs(matrix[5]); + result[6] = Math.abs(matrix[6]); + result[7] = Math.abs(matrix[7]); + result[8] = Math.abs(matrix[8]); + + return result; + }; + + /** + * Computes the determinant of the provided matrix. + * @memberof Matrix3 + * + * @param {Matrix3} matrix The matrix to use. + * @returns {Number} The value of the determinant of the matrix. + * + * @exception {DeveloperError} matrix is required. + */ + Matrix3.determinant = function(matrix) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + + var m11 = matrix[0]; + var m21 = matrix[3]; + var m31 = matrix[6]; + var m12 = matrix[1]; + var m22 = matrix[4]; + var m32 = matrix[7]; + var m13 = matrix[2]; + var m23 = matrix[5]; + var m33 = matrix[8]; + + return m11 * (m22 * m33 - m23 * m32) + m12 * (m23 * m31 - m21 * m33) + m13 * (m21 * m32 - m22 * m31); + }; + + /** + * Computes the inverse of the provided matrix. + * @memberof Matrix3 + * + * @param {Matrix3} matrix The matrix to invert. + * @param {Matrix3} [result] The object onto which to store the result. + * @returns {Matrix3} The modified result parameter or a new Matrix3 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} matrix is not invertible. + */ + Matrix3.inverse = function(matrix, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + + var m11 = matrix[0]; + var m21 = matrix[1]; + var m31 = matrix[2]; + var m12 = matrix[3]; + var m22 = matrix[4]; + var m32 = matrix[5]; + var m13 = matrix[6]; + var m23 = matrix[7]; + var m33 = matrix[8]; + + var determinant = Matrix3.determinant(matrix); + + if (Math.abs(determinant) <= CesiumMath.EPSILON15) { + throw new DeveloperError('matrix is not invertible'); + } + + var m = new Matrix3(m22 * m33 - m23 * m32, m13 * m32 - m12 * m33, m12 * m23 - m13 * m22, + m23 * m31 - m21 * m33, m11 * m33 - m13 * m31, m13 * m21 - m11 * m23, + m21 * m32 - m22 * m31, m12 * m31 - m11 * m32, m11 * m22 - m12 * m21); + var scale = 1.0 / determinant; + return Matrix3.multiplyByScalar(m, scale, result); + }; + + /** + * Compares the provided matrices componentwise and returns + * <code>true</code> if they are equal, <code>false</code> otherwise. + * @memberof Matrix3 + * + * @param {Matrix3} [left] The first matrix. + * @param {Matrix3} [right] The second matrix. + * @returns {Boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise. + */ + Matrix3.equals = function(left, right) { + return (left === right) || + (defined(left) && + defined(right) && + left[0] === right[0] && + left[1] === right[1] && + left[2] === right[2] && + left[3] === right[3] && + left[4] === right[4] && + left[5] === right[5] && + left[6] === right[6] && + left[7] === right[7] && + left[8] === right[8]); + }; + + /** + * Compares the provided matrices componentwise and returns + * <code>true</code> if they are within the provided epsilon, + * <code>false</code> otherwise. + * @memberof Matrix3 + * + * @param {Matrix3} [left] The first matrix. + * @param {Matrix3} [right] The second matrix. + * @param {Number} epsilon The epsilon to use for equality testing. + * @returns {Boolean} <code>true</code> if left and right are within the provided epsilon, <code>false</code> otherwise. + * + * @exception {DeveloperError} epsilon is required and must be a number. + */ + Matrix3.equalsEpsilon = function(left, right, epsilon) { + if (typeof epsilon !== 'number') { + throw new DeveloperError('epsilon is required and must be a number'); + } + + return (left === right) || + (defined(left) && + defined(right) && + Math.abs(left[0] - right[0]) <= epsilon && + Math.abs(left[1] - right[1]) <= epsilon && + Math.abs(left[2] - right[2]) <= epsilon && + Math.abs(left[3] - right[3]) <= epsilon && + Math.abs(left[4] - right[4]) <= epsilon && + Math.abs(left[5] - right[5]) <= epsilon && + Math.abs(left[6] - right[6]) <= epsilon && + Math.abs(left[7] - right[7]) <= epsilon && + Math.abs(left[8] - right[8]) <= epsilon); + }; + + /** + * An immutable Matrix3 instance initialized to the identity matrix. + * @memberof Matrix3 + */ + Matrix3.IDENTITY = freezeObject(new Matrix3(1.0, 0.0, 0.0, + 0.0, 1.0, 0.0, + 0.0, 0.0, 1.0)); + + /** + * The index into Matrix3 for column 0, row 0. + * @memberof Matrix3 + */ + Matrix3.COLUMN0ROW0 = 0; + + /** + * The index into Matrix3 for column 0, row 1. + * @memberof Matrix3 + */ + Matrix3.COLUMN0ROW1 = 1; + + /** + * The index into Matrix3 for column 0, row 2. + * @memberof Matrix3 + */ + Matrix3.COLUMN0ROW2 = 2; + + /** + * The index into Matrix3 for column 1, row 0. + * @memberof Matrix3 + */ + Matrix3.COLUMN1ROW0 = 3; + + /** + * The index into Matrix3 for column 1, row 1. + * @memberof Matrix3 + */ + Matrix3.COLUMN1ROW1 = 4; + + /** + * The index into Matrix3 for column 1, row 2. + * @memberof Matrix3 + */ + Matrix3.COLUMN1ROW2 = 5; + + /** + * The index into Matrix3 for column 2, row 0. + * @memberof Matrix3 + */ + Matrix3.COLUMN2ROW0 = 6; + + /** + * The index into Matrix3 for column 2, row 1. + * @memberof Matrix3 + */ + Matrix3.COLUMN2ROW1 = 7; + + /** + * The index into Matrix3 for column 2, row 2. + * @memberof Matrix3 + */ + Matrix3.COLUMN2ROW2 = 8; + + /** + * Duplicates the provided Matrix3 instance. + * @memberof Matrix3 + * + * @param {Matrix3} [result] The object onto which to store the result. + * @returns {Matrix3} The modified result parameter or a new Matrix3 instance if one was not provided. + */ + Matrix3.prototype.clone = function(result) { + return Matrix3.clone(this, result); + }; + + /** + * Compares this matrix to the provided matrix componentwise and returns + * <code>true</code> if they are equal, <code>false</code> otherwise. + * @memberof Matrix3 + * + * @param {Matrix3} [right] The right hand side matrix. + * @returns {Boolean} <code>true</code> if they are equal, <code>false</code> otherwise. + */ + Matrix3.prototype.equals = function(right) { + return Matrix3.equals(this, right); + }; + + /** + * Compares this matrix to the provided matrix componentwise and returns + * <code>true</code> if they are within the provided epsilon, + * <code>false</code> otherwise. + * @memberof Matrix3 + * + * @param {Matrix3} [right] The right hand side matrix. + * @param {Number} epsilon The epsilon to use for equality testing. + * @returns {Boolean} <code>true</code> if they are within the provided epsilon, <code>false</code> otherwise. + * + * @exception {DeveloperError} epsilon is required and must be a number. + */ + Matrix3.prototype.equalsEpsilon = function(right, epsilon) { + return Matrix3.equalsEpsilon(this, right, epsilon); + }; + + /** + * Creates a string representing this Matrix with each row being + * on a separate line and in the format '(column0, column1, column2)'. + * @memberof Matrix3 + * + * @returns {String} A string representing the provided Matrix with each row being on a separate line and in the format '(column0, column1, column2)'. + */ + Matrix3.prototype.toString = function() { + return '(' + this[0] + ', ' + this[3] + ', ' + this[6] + ')\n' + + '(' + this[1] + ', ' + this[4] + ', ' + this[7] + ')\n' + + '(' + this[2] + ', ' + this[5] + ', ' + this[8] + ')'; + }; + + return Matrix3; +}); + +/*global define*/ +define('Core/RuntimeError',['./defined'], function(defined) { + "use strict"; + + /** + * Constructs an exception object that is thrown due to an error that can occur at runtime, e.g., + * out of memory, could not compile shader, etc. If a function may throw this + * exception, the calling code should be prepared to catch it. + * <br /><br /> + * On the other hand, a {@link DeveloperError} indicates an exception due + * to a developer error, e.g., invalid argument, that usually indicates a bug in the + * calling code. + * + * @alias RuntimeError + * + * @param {String} [message=undefined] The error message for this exception. + * + * @see DeveloperError + * @constructor + */ + var RuntimeError = function(message) { + /** + * 'RuntimeError' indicating that this exception was thrown due to a runtime error. + * @type {String} + * @constant + * @default + */ + this.name = 'RuntimeError'; + + /** + * The explanation for why this exception was thrown. + * @type {String} + * @constant + */ + this.message = message; + + var e = new Error(); + + /** + * The stack trace of this exception, if available. + * @type {String} + * @constant + */ + this.stack = e.stack; + }; + + RuntimeError.prototype.toString = function() { + var str = this.name + ': ' + this.message; + + if (defined(this.stack)) { + str += '\n' + this.stack.toString(); + } + + return str; + }; + + return RuntimeError; +}); + +/*global define*/ +define('Core/Matrix4',[ + './Cartesian3', + './Cartesian4', + './defaultValue', + './defined', + './DeveloperError', + './freezeObject', + './Math', + './Matrix3', + './RuntimeError' + ], function( + Cartesian3, + Cartesian4, + defaultValue, + defined, + DeveloperError, + freezeObject, + CesiumMath, + Matrix3, + RuntimeError) { + "use strict"; + + /** + * A 4x4 matrix, indexable as a column-major order array. + * Constructor parameters are in row-major order for code readability. + * @alias Matrix4 + * @constructor + * + * @param {Number} [column0Row0=0.0] The value for column 0, row 0. + * @param {Number} [column1Row0=0.0] The value for column 1, row 0. + * @param {Number} [column2Row0=0.0] The value for column 2, row 0. + * @param {Number} [column3Row0=0.0] The value for column 3, row 0. + * @param {Number} [column0Row1=0.0] The value for column 0, row 1. + * @param {Number} [column1Row1=0.0] The value for column 1, row 1. + * @param {Number} [column2Row1=0.0] The value for column 2, row 1. + * @param {Number} [column3Row1=0.0] The value for column 3, row 1. + * @param {Number} [column0Row2=0.0] The value for column 0, row 2. + * @param {Number} [column1Row2=0.0] The value for column 1, row 2. + * @param {Number} [column2Row2=0.0] The value for column 2, row 2. + * @param {Number} [column3Row2=0.0] The value for column 3, row 2. + * @param {Number} [column0Row3=0.0] The value for column 0, row 3. + * @param {Number} [column1Row3=0.0] The value for column 1, row 3. + * @param {Number} [column2Row3=0.0] The value for column 2, row 3. + * @param {Number} [column3Row3=0.0] The value for column 3, row 3. + * + * @see Matrix4.fromColumnMajorArray + * @see Matrix4.fromRowMajorArray + * @see Matrix4.fromRotationTranslation + * @see Matrix4.fromTranslationQuaternionRotationScale + * @see Matrix4.fromTranslation + * @see Matrix4.fromScale + * @see Matrix4.fromUniformScale + * @see Matrix4.fromCamera + * @see Matrix4.computePerspectiveFieldOfView + * @see Matrix4.computeOrthographicOffCenter + * @see Matrix4.computePerspectiveOffCenter + * @see Matrix4.computeInfinitePerspectiveOffCenter + * @see Matrix4.computeViewportTransformation + * @see Matrix2 + * @see Matrix3 + */ + var Matrix4 = function(column0Row0, column1Row0, column2Row0, column3Row0, + column0Row1, column1Row1, column2Row1, column3Row1, + column0Row2, column1Row2, column2Row2, column3Row2, + column0Row3, column1Row3, column2Row3, column3Row3) { + this[0] = defaultValue(column0Row0, 0.0); + this[1] = defaultValue(column0Row1, 0.0); + this[2] = defaultValue(column0Row2, 0.0); + this[3] = defaultValue(column0Row3, 0.0); + this[4] = defaultValue(column1Row0, 0.0); + this[5] = defaultValue(column1Row1, 0.0); + this[6] = defaultValue(column1Row2, 0.0); + this[7] = defaultValue(column1Row3, 0.0); + this[8] = defaultValue(column2Row0, 0.0); + this[9] = defaultValue(column2Row1, 0.0); + this[10] = defaultValue(column2Row2, 0.0); + this[11] = defaultValue(column2Row3, 0.0); + this[12] = defaultValue(column3Row0, 0.0); + this[13] = defaultValue(column3Row1, 0.0); + this[14] = defaultValue(column3Row2, 0.0); + this[15] = defaultValue(column3Row3, 0.0); + }; + + /** + * Duplicates a Matrix4 instance. + * @memberof Matrix4 + * + * @param {Matrix4} matrix The matrix to duplicate. + * @param {Matrix4} [result] The object onto which to store the result. + * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if one was not provided. (Returns undefined if matrix is undefined) + */ + Matrix4.clone = function(matrix, result) { + if (!defined(matrix)) { + return undefined; + } + if (!defined(result)) { + return new Matrix4(matrix[0], matrix[4], matrix[8], matrix[12], + matrix[1], matrix[5], matrix[9], matrix[13], + matrix[2], matrix[6], matrix[10], matrix[14], + matrix[3], matrix[7], matrix[11], matrix[15]); + } + result[0] = matrix[0]; + result[1] = matrix[1]; + result[2] = matrix[2]; + result[3] = matrix[3]; + result[4] = matrix[4]; + result[5] = matrix[5]; + result[6] = matrix[6]; + result[7] = matrix[7]; + result[8] = matrix[8]; + result[9] = matrix[9]; + result[10] = matrix[10]; + result[11] = matrix[11]; + result[12] = matrix[12]; + result[13] = matrix[13]; + result[14] = matrix[14]; + result[15] = matrix[15]; + return result; + }; + + /** + * Creates a Matrix4 from 16 consecutive elements in an array. + * @memberof Matrix4 + * + * @param {Array} array The array whose 16 consecutive elements correspond to the positions of the matrix. Assumes column-major order. + * @param {Number} [startingIndex=0] The offset into the array of the first element, which corresponds to first column first row position in the matrix. + * @param {Matrix4} [result] The object onto which to store the result. + * + * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} array is required. + * + * @example + * // Create the Matrix4: + * // [1.0, 2.0, 3.0, 4.0] + * // [1.0, 2.0, 3.0, 4.0] + * // [1.0, 2.0, 3.0, 4.0] + * // [1.0, 2.0, 3.0, 4.0] + * + * var v = [1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, 3.0, 4.0, 4.0, 4.0, 4.0]; + * var m = Matrix4.fromArray(v); + * + * // Create same Matrix4 with using an offset into an array + * var v2 = [0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, 3.0, 4.0, 4.0, 4.0, 4.0]; + * var m2 = Matrix4.fromArray(v2, 2); + */ + Matrix4.fromArray = function(array, startingIndex, result) { + if (!defined(array)) { + throw new DeveloperError('array is required'); + } + + startingIndex = defaultValue(startingIndex, 0); + + if (!defined(result)) { + result = new Matrix4(); + } + + result[0] = array[startingIndex]; + result[1] = array[startingIndex + 1]; + result[2] = array[startingIndex + 2]; + result[3] = array[startingIndex + 3]; + result[4] = array[startingIndex + 4]; + result[5] = array[startingIndex + 5]; + result[6] = array[startingIndex + 6]; + result[7] = array[startingIndex + 7]; + result[8] = array[startingIndex + 8]; + result[9] = array[startingIndex + 9]; + result[10] = array[startingIndex + 10]; + result[11] = array[startingIndex + 11]; + result[12] = array[startingIndex + 12]; + result[13] = array[startingIndex + 13]; + result[14] = array[startingIndex + 14]; + result[15] = array[startingIndex + 15]; + return result; + }; + + /** + * Computes a Matrix4 instance from a column-major order array. + * @memberof Matrix4 + * @function + * + * @param {Array} values The column-major order array. + * @param {Matrix4} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} values is required. + */ + Matrix4.fromColumnMajorArray = function(values, result) { + if (!defined(values)) { + throw new DeveloperError('values parameter is required'); + } + + return Matrix4.clone(values, result); + }; + + /** + * Computes a Matrix4 instance from a row-major order array. + * The resulting matrix will be in column-major order. + * @memberof Matrix4 + * + * @param {Array} values The row-major order array. + * @param {Matrix4} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} values is required. + */ + Matrix4.fromRowMajorArray = function(values, result) { + if (!defined(values)) { + throw new DeveloperError('values is required.'); + } + + if (!defined(result)) { + return new Matrix4(values[0], values[1], values[2], values[3], + values[4], values[5], values[6], values[7], + values[8], values[9], values[10], values[11], + values[12], values[13], values[14], values[15]); + } + result[0] = values[0]; + result[1] = values[4]; + result[2] = values[8]; + result[3] = values[12]; + result[4] = values[1]; + result[5] = values[5]; + result[6] = values[9]; + result[7] = values[13]; + result[8] = values[2]; + result[9] = values[6]; + result[10] = values[10]; + result[11] = values[14]; + result[12] = values[3]; + result[13] = values[7]; + result[14] = values[11]; + result[15] = values[15]; + return result; + }; + + /** + * Computes a Matrix4 instance from a Matrix3 representing the rotation + * and a Cartesian3 representing the translation. + * @memberof Matrix4 + * + * @param {Matrix3} rotation The upper left portion of the matrix representing the rotation. + * @param {Cartesian3} translation The upper right portion of the matrix representing the translation. + * @param {Matrix4} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} rotation is required. + * @exception {DeveloperError} translation is required. + */ + Matrix4.fromRotationTranslation = function(rotation, translation, result) { + if (!defined(rotation)) { + throw new DeveloperError('rotation is required.'); + } + if (!defined(translation)) { + throw new DeveloperError('translation is required.'); + } + + if (!defined(result)) { + return new Matrix4(rotation[0], rotation[3], rotation[6], translation.x, + rotation[1], rotation[4], rotation[7], translation.y, + rotation[2], rotation[5], rotation[8], translation.z, + 0.0, 0.0, 0.0, 1.0); + } + + result[0] = rotation[0]; + result[1] = rotation[1]; + result[2] = rotation[2]; + result[3] = 0.0; + result[4] = rotation[3]; + result[5] = rotation[4]; + result[6] = rotation[5]; + result[7] = 0.0; + result[8] = rotation[6]; + result[9] = rotation[7]; + result[10] = rotation[8]; + result[11] = 0.0; + result[12] = translation.x; + result[13] = translation.y; + result[14] = translation.z; + result[15] = 1.0; + return result; + }; + + var scratchTrsRotation = new Matrix3(); + + /** + * Computes a Matrix4 instance from a translation, rotation, and scale (TRS) + * representation with the rotation represented as a quaternion. + * + * @memberof Matrix4 + * + * @param {Cartesian3} translation The translation transformation. + * @param {Quaternion} rotation The rotation transformation. + * @param {Cartesian3} scale The non-uniform scale transformation. + * @param {Matrix4} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} translation is required. + * @exception {DeveloperError} rotation is required. + * @exception {DeveloperError} scale is required. + * + * @example + * result = Matrix4.fromTranslationQuaternionRotationScale( + * new Cartesian3(1.0, 2.0, 3.0), // translation + * Quaternion.IDENTITY, // rotation + * new Cartesian3(7.0, 8.0, 9.0), // scale + * result); + */ + Matrix4.fromTranslationQuaternionRotationScale = function(translation, rotation, scale, result) { + if (!defined(translation)) { + throw new DeveloperError('translation is required.'); + } + if (!defined(rotation)) { + throw new DeveloperError('rotation is required.'); + } + if (!defined(scale)) { + throw new DeveloperError('scale is required.'); + } + + if (!defined(result)) { + result = new Matrix4(); + } + + var scaleX = scale.x; + var scaleY = scale.y; + var scaleZ = scale.z; + + var x2 = rotation.x * rotation.x; + var xy = rotation.x * rotation.y; + var xz = rotation.x * rotation.z; + var xw = rotation.x * rotation.w; + var y2 = rotation.y * rotation.y; + var yz = rotation.y * rotation.z; + var yw = rotation.y * rotation.w; + var z2 = rotation.z * rotation.z; + var zw = rotation.z * rotation.w; + var w2 = rotation.w * rotation.w; + + var m00 = x2 - y2 - z2 + w2; + var m01 = 2.0 * (xy - zw); + var m02 = 2.0 * (xz + yw); + + var m10 = 2.0 * (xy + zw); + var m11 = -x2 + y2 - z2 + w2; + var m12 = 2.0 * (yz - xw); + + var m20 = 2.0 * (xz - yw); + var m21 = 2.0 * (yz + xw); + var m22 = -x2 - y2 + z2 + w2; + + result[0] = m00 * scaleX; + result[1] = m10 * scaleX; + result[2] = m20 * scaleX; + result[3] = 0.0; + result[4] = m01 * scaleY; + result[5] = m11 * scaleY; + result[6] = m21 * scaleY; + result[7] = 0.0; + result[8] = m02 * scaleZ; + result[9] = m12 * scaleZ; + result[10] = m22 * scaleZ; + result[11] = 0.0; + result[12] = translation.x; + result[13] = translation.y; + result[14] = translation.z; + result[15] = 1.0; + + return result; + }; + + /** + * Creates a Matrix4 instance from a Cartesian3 representing the translation. + * @memberof Matrix4 + * + * @param {Cartesian3} translation The upper right portion of the matrix representing the translation. + * @param {Matrix4} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix4 instance if one was not provided. + * + * @see Matrix4.multiplyByTranslation + * + * @exception {DeveloperError} translation is required. + */ + Matrix4.fromTranslation = function(translation, result) { + return Matrix4.fromRotationTranslation(Matrix3.IDENTITY, translation, result); + }; + + /** + * Computes a Matrix4 instance representing a non-uniform scale. + * @memberof Matrix4 + * + * @param {Cartesian3} scale The x, y, and z scale factors. + * @param {Matrix4} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} scale is required. + * + * @example + * // Creates + * // [7.0, 0.0, 0.0, 0.0] + * // [0.0, 8.0, 0.0, 0.0] + * // [0.0, 0.0, 9.0, 0.0] + * // [0.0, 0.0, 0.0, 1.0] + * var m = Matrix4.fromScale(new Cartesian3(7.0, 8.0, 9.0)); + */ + Matrix4.fromScale = function(scale, result) { + if (!defined(scale)) { + throw new DeveloperError('scale is required.'); + } + + if (!defined(result)) { + return new Matrix4( + scale.x, 0.0, 0.0, 0.0, + 0.0, scale.y, 0.0, 0.0, + 0.0, 0.0, scale.z, 0.0, + 0.0, 0.0, 0.0, 1.0); + } + + result[0] = scale.x; + result[1] = 0.0; + result[2] = 0.0; + result[3] = 0.0; + result[4] = 0.0; + result[5] = scale.y; + result[6] = 0.0; + result[7] = 0.0; + result[8] = 0.0; + result[9] = 0.0; + result[10] = scale.z; + result[11] = 0.0; + result[12] = 0.0; + result[13] = 0.0; + result[14] = 0.0; + result[15] = 1.0; + return result; + }; + + /** + * Computes a Matrix4 instance representing a uniform scale. + * @memberof Matrix4 + * + * @param {Number} scale The uniform scale factor. + * @param {Matrix4} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} scale is required. + * + * @example + * // Creates + * // [2.0, 0.0, 0.0, 0.0] + * // [0.0, 2.0, 0.0, 0.0] + * // [0.0, 0.0, 2.0, 0.0] + * // [0.0, 0.0, 0.0, 1.0] + * var m = Matrix4.fromScale(2.0); + */ + Matrix4.fromUniformScale = function(scale, result) { + if (typeof scale !== 'number') { + throw new DeveloperError('scale is required.'); + } + + if (!defined(result)) { + return new Matrix4(scale, 0.0, 0.0, 0.0, + 0.0, scale, 0.0, 0.0, + 0.0, 0.0, scale, 0.0, + 0.0, 0.0, 0.0, 1.0); + } + + result[0] = scale; + result[1] = 0.0; + result[2] = 0.0; + result[3] = 0.0; + result[4] = 0.0; + result[5] = scale; + result[6] = 0.0; + result[7] = 0.0; + result[8] = 0.0; + result[9] = 0.0; + result[10] = scale; + result[11] = 0.0; + result[12] = 0.0; + result[13] = 0.0; + result[14] = 0.0; + result[15] = 1.0; + return result; + }; + + var fromCameraF = new Cartesian3(); + var fromCameraS = new Cartesian3(); + var fromCameraU = new Cartesian3(); + + /** + * Computes a Matrix4 instance from a Camera. + * @memberof Matrix4 + * + * @param {Camera} camera The camera to use. + * @param {Matrix4} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} camera is required. + * @exception {DeveloperError} camera.eye is required. + * @exception {DeveloperError} camera.target is required. + * @exception {DeveloperError} camera.up is required. + */ + Matrix4.fromCamera = function(camera, result) { + if (!defined(camera)) { + throw new DeveloperError('camera is required.'); + } + + var eye = camera.eye; + var target = camera.target; + var up = camera.up; + + if (!defined(eye)) { + throw new DeveloperError('camera.eye is required.'); + } + if (!defined(target)) { + throw new DeveloperError('camera.target is required.'); + } + if (!defined(up)) { + throw new DeveloperError('camera.up is required.'); + } + + Cartesian3.normalize(Cartesian3.subtract(target, eye, fromCameraF), fromCameraF); + Cartesian3.normalize(Cartesian3.cross(fromCameraF, up, fromCameraS), fromCameraS); + Cartesian3.normalize(Cartesian3.cross(fromCameraS, fromCameraF, fromCameraU), fromCameraU); + + var sX = fromCameraS.x; + var sY = fromCameraS.y; + var sZ = fromCameraS.z; + var fX = fromCameraF.x; + var fY = fromCameraF.y; + var fZ = fromCameraF.z; + var uX = fromCameraU.x; + var uY = fromCameraU.y; + var uZ = fromCameraU.z; + var eyeX = eye.x; + var eyeY = eye.y; + var eyeZ = eye.z; + var t0 = sX * -eyeX + sY * -eyeY+ sZ * -eyeZ; + var t1 = uX * -eyeX + uY * -eyeY+ uZ * -eyeZ; + var t2 = fX * eyeX + fY * eyeY + fZ * eyeZ; + + //The code below this comment is an optimized + //version of the commented lines. + //Rather that create two matrices and then multiply, + //we just bake in the multiplcation as part of creation. + //var rotation = new Matrix4( + // sX, sY, sZ, 0.0, + // uX, uY, uZ, 0.0, + // -fX, -fY, -fZ, 0.0, + // 0.0, 0.0, 0.0, 1.0); + //var translation = new Matrix4( + // 1.0, 0.0, 0.0, -eye.x, + // 0.0, 1.0, 0.0, -eye.y, + // 0.0, 0.0, 1.0, -eye.z, + // 0.0, 0.0, 0.0, 1.0); + //return rotation.multiply(translation); + if (!defined(result)) { + return new Matrix4( + sX, sY, sZ, t0, + uX, uY, uZ, t1, + -fX, -fY, -fZ, t2, + 0.0, 0.0, 0.0, 1.0); + } + result[0] = sX; + result[1] = uX; + result[2] = -fX; + result[3] = 0.0; + result[4] = sY; + result[5] = uY; + result[6] = -fY; + result[7] = 0.0; + result[8] = sZ; + result[9] = uZ; + result[10] = -fZ; + result[11] = 0.0; + result[12] = t0; + result[13] = t1; + result[14] = t2; + result[15] = 1.0; + return result; + + }; + + /** + * Computes a Matrix4 instance representing a perspective transformation matrix. + * @memberof Matrix4 + * + * @param {Number} fovY The field of view along the Y axis in radians. + * @param {Number} aspectRatio The aspect ratio. + * @param {Number} near The distance to the near plane in meters. + * @param {Number} far The distance to the far plane in meters. + * @param {Matrix4} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} fovY must be in [0, PI). + * @exception {DeveloperError} aspectRatio must be greater than zero. + * @exception {DeveloperError} near must be greater than zero. + * @exception {DeveloperError} far must be greater than zero. + */ + Matrix4.computePerspectiveFieldOfView = function(fovY, aspectRatio, near, far, result) { + if (fovY <= 0.0 || fovY > Math.PI) { + throw new DeveloperError('fovY must be in [0, PI).'); + } + + if (aspectRatio <= 0.0) { + throw new DeveloperError('aspectRatio must be greater than zero.'); + } + + if (near <= 0.0) { + throw new DeveloperError('near must be greater than zero.'); + } + + if (far <= 0.0) { + throw new DeveloperError('far must be greater than zero.'); + } + + var bottom = Math.tan(fovY * 0.5); + + var column1Row1 = 1.0 / bottom; + var column0Row0 = column1Row1 / aspectRatio; + var column2Row2 = (far + near) / (near - far); + var column3Row2 = (2.0 * far * near) / (near - far); + + if (!defined(result)) { + return new Matrix4(column0Row0, 0.0, 0.0, 0.0, + 0.0, column1Row1, 0.0, 0.0, + 0.0, 0.0, column2Row2, column3Row2, + 0.0, 0.0, -1.0, 0.0); + } + + result[0] = column0Row0; + result[1] = 0.0; + result[2] = 0.0; + result[3] = 0.0; + result[4] = 0.0; + result[5] = column1Row1; + result[6] = 0.0; + result[7] = 0.0; + result[8] = 0.0; + result[9] = 0.0; + result[10] = column2Row2; + result[11] = -1.0; + result[12] = 0.0; + result[13] = 0.0; + result[14] = column3Row2; + result[15] = 0.0; + return result; + }; + + /** + * Computes a Matrix4 instance representing an orthographic transformation matrix. + * @memberof Matrix4 + * + * @param {Number} left The number of meters to the left of the camera that will be in view. + * @param {Number} right The number of meters to the right of the camera that will be in view. + * @param {Number} bottom The number of meters below of the camera that will be in view. + * @param {Number} top The number of meters above of the camera that will be in view. + * @param {Number} near The distance to the near plane in meters. + * @param {Number} far The distance to the far plane in meters. + * @param {Matrix4} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + * @exception {DeveloperError} bottom is required. + * @exception {DeveloperError} top is required. + * @exception {DeveloperError} near is required. + * @exception {DeveloperError} far is required. + */ + Matrix4.computeOrthographicOffCenter = function(left, right, bottom, top, near, far, result) { + if (!defined(left)) { + throw new DeveloperError('left is required.'); + } + if (!defined(right)) { + throw new DeveloperError('right is required.'); + } + if (!defined(bottom)) { + throw new DeveloperError('bottom is required.'); + } + if (!defined(top)) { + throw new DeveloperError('top is required.'); + } + if (!defined(near)) { + throw new DeveloperError('near is required.'); + } + if (!defined(far)) { + throw new DeveloperError('far is required.'); + } + + var a = 1.0 / (right - left); + var b = 1.0 / (top - bottom); + var c = 1.0 / (far - near); + + var tx = -(right + left) * a; + var ty = -(top + bottom) * b; + var tz = -(far + near) * c; + a *= 2.0; + b *= 2.0; + c *= -2.0; + + if (!defined(result)) { + return new Matrix4( a, 0.0, 0.0, tx, + 0.0, b, 0.0, ty, + 0.0, 0.0, c, tz, + 0.0, 0.0, 0.0, 1.0); + } + + result[0] = a; + result[1] = 0.0; + result[2] = 0.0; + result[3] = 0.0; + result[4] = 0.0; + result[5] = b; + result[6] = 0.0; + result[7] = 0.0; + result[8] = 0.0; + result[9] = 0.0; + result[10] = c; + result[11] = 0.0; + result[12] = tx; + result[13] = ty; + result[14] = tz; + result[15] = 1.0; + return result; + }; + + /** + * Computes a Matrix4 instance representing an off center perspective transformation. + * @memberof Matrix4 + * + * @param {Number} left The number of meters to the left of the camera that will be in view. + * @param {Number} right The number of meters to the right of the camera that will be in view. + * @param {Number} bottom The number of meters below of the camera that will be in view. + * @param {Number} top The number of meters above of the camera that will be in view. + * @param {Number} near The distance to the near plane in meters. + * @param {Number} far The distance to the far plane in meters. + * @param {Matrix4} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + * @exception {DeveloperError} bottom is required. + * @exception {DeveloperError} top is required. + * @exception {DeveloperError} near is required. + * @exception {DeveloperError} far is required. + */ + Matrix4.computePerspectiveOffCenter = function(left, right, bottom, top, near, far, result) { + if (!defined(left)) { + throw new DeveloperError('left is required.'); + } + if (!defined(right)) { + throw new DeveloperError('right is required.'); + } + if (!defined(bottom)) { + throw new DeveloperError('bottom is required.'); + } + if (!defined(top)) { + throw new DeveloperError('top is required.'); + } + if (!defined(near)) { + throw new DeveloperError('near is required.'); + } + if (!defined(far)) { + throw new DeveloperError('far is required.'); + } + + var column0Row0 = 2.0 * near / (right - left); + var column1Row1 = 2.0 * near / (top - bottom); + var column2Row0 = (right + left) / (right - left); + var column2Row1 = (top + bottom) / (top - bottom); + var column2Row2 = -(far + near) / (far - near); + var column2Row3 = -1.0; + var column3Row2 = -2.0 * far * near / (far - near); + + if (!defined(result)) { + return new Matrix4(column0Row0, 0.0, column2Row0, 0.0, + 0.0, column1Row1, column2Row1, 0.0, + 0.0, 0.0, column2Row2, column3Row2, + 0.0, 0.0, column2Row3, 0.0); + } + + result[0] = column0Row0; + result[1] = 0.0; + result[2] = 0.0; + result[3] = 0.0; + result[4] = 0.0; + result[5] = column1Row1; + result[6] = 0.0; + result[7] = 0.0; + result[8] = column2Row0; + result[9] = column2Row1; + result[10] = column2Row2; + result[11] = column2Row3; + result[12] = 0.0; + result[13] = 0.0; + result[14] = column3Row2; + result[15] = 0.0; + return result; + }; + + /** + * Computes a Matrix4 instance representing an infinite off center perspective transformation. + * @memberof Matrix4 + * + * @param {Number} left The number of meters to the left of the camera that will be in view. + * @param {Number} right The number of meters to the right of the camera that will be in view. + * @param {Number} bottom The number of meters below of the camera that will be in view. + * @param {Number} top The number of meters above of the camera that will be in view. + * @param {Number} near The distance to the near plane in meters. + * @param {Number} far The distance to the far plane in meters. + * @param {Matrix4} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + * @exception {DeveloperError} bottom is required. + * @exception {DeveloperError} top is required. + * @exception {DeveloperError} near is required. + */ + Matrix4.computeInfinitePerspectiveOffCenter = function(left, right, bottom, top, near, result) { + if (!defined(left)) { + throw new DeveloperError('left is required.'); + } + if (!defined(right)) { + throw new DeveloperError('right is required.'); + } + if (!defined(bottom)) { + throw new DeveloperError('bottom is required.'); + } + if (!defined(top)) { + throw new DeveloperError('top is required.'); + } + if (!defined(near)) { + throw new DeveloperError('near is required.'); + } + + var column0Row0 = 2.0 * near / (right - left); + var column1Row1 = 2.0 * near / (top - bottom); + var column2Row0 = (right + left) / (right - left); + var column2Row1 = (top + bottom) / (top - bottom); + var column2Row2 = -1.0; + var column2Row3 = -1.0; + var column3Row2 = -2.0 * near; + + if (!defined(result)) { + return new Matrix4(column0Row0, 0.0, column2Row0, 0.0, + 0.0, column1Row1, column2Row1, 0.0, + 0.0, 0.0, column2Row2, column3Row2, + 0.0, 0.0, column2Row3, 0.0); + } + + result[0] = column0Row0; + result[1] = 0.0; + result[2] = 0.0; + result[3] = 0.0; + result[4] = 0.0; + result[5] = column1Row1; + result[6] = 0.0; + result[7] = 0.0; + result[8] = column2Row0; + result[9] = column2Row1; + result[10] = column2Row2; + result[11] = column2Row3; + result[12] = 0.0; + result[13] = 0.0; + result[14] = column3Row2; + result[15] = 0.0; + return result; + }; + + /** + * Computes a Matrix4 instance that transforms from normalized device coordinates to window coordinates. + * @memberof Matrix4 + * + * @param {Object}[viewport = { x : 0.0, y : 0.0, width : 0.0, height : 0.0 }] The viewport's corners as shown in Example 1. + * @param {Number}[nearDepthRange = 0.0] The near plane distance in window coordinates. + * @param {Number}[farDepthRange = 1.0] The far plane distance in window coordinates. + * @param {Matrix4} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix4 instance if one was not provided. + * + * @see czm_viewportTransformation + * @see Context#getViewport + * + * @example + * // Example 1. Create viewport transformation using an explicit viewport and depth range. + * var m = Matrix4.computeViewportTransformation({ + * x : 0.0, + * y : 0.0, + * width : 1024.0, + * height : 768.0 + * }, 0.0, 1.0); + * + * // Example 2. Create viewport transformation using the context's viewport. + * var m = Matrix4.computeViewportTransformation(context.getViewport()); + */ + Matrix4.computeViewportTransformation = function(viewport, nearDepthRange, farDepthRange, result) { + viewport = defaultValue(viewport, defaultValue.EMPTY_OBJECT); + var x = defaultValue(viewport.x, 0.0); + var y = defaultValue(viewport.y, 0.0); + var width = defaultValue(viewport.width, 0.0); + var height = defaultValue(viewport.height, 0.0); + nearDepthRange = defaultValue(nearDepthRange, 0.0); + farDepthRange = defaultValue(farDepthRange, 1.0); + + var halfWidth = width * 0.5; + var halfHeight = height * 0.5; + var halfDepth = (farDepthRange - nearDepthRange) * 0.5; + + var column0Row0 = halfWidth; + var column1Row1 = halfHeight; + var column2Row2 = halfDepth; + var column3Row0 = x + halfWidth; + var column3Row1 = y + halfHeight; + var column3Row2 = nearDepthRange + halfDepth; + var column3Row3 = 1.0; + + if (!defined(result)) { + return new Matrix4(column0Row0, 0.0, 0.0, column3Row0, + 0.0, column1Row1, 0.0, column3Row1, + 0.0, 0.0, column2Row2, column3Row2, + 0.0, 0.0, 0.0, column3Row3); + } + result[0] = column0Row0; + result[1] = 0.0; + result[2] = 0.0; + result[3] = 0.0; + result[4] = 0.0; + result[5] = column1Row1; + result[6] = 0.0; + result[7] = 0.0; + result[8] = 0.0; + result[9] = 0.0; + result[10] = column2Row2; + result[11] = 0.0; + result[12] = column3Row0; + result[13] = column3Row1; + result[14] = column3Row2; + result[15] = column3Row3; + return result; + }; + + /** + * Computes an Array from the provided Matrix4 instance. + * The array will be in column-major order. + * @memberof Matrix4 + * + * @param {Matrix4} matrix The matrix to use.. + * @param {Array} [result] The Array onto which to store the result. + * @returns {Array} The modified Array parameter or a new Array instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * + * @example + * //create an array from an instance of Matrix4 + * // m = [10.0, 14.0, 18.0, 22.0] + * // [11.0, 15.0, 19.0, 23.0] + * // [12.0, 16.0, 20.0, 24.0] + * // [13.0, 17.0, 21.0, 25.0] + * var a = Matrix4.toArray(m); + * + * // m remains the same + * //creates a = [10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0, 19.0, 20.0, 21.0, 22.0, 23.0, 24.0, 25.0] + * + */ + Matrix4.toArray = function(matrix, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + + if (!defined(result)) { + return [matrix[0], matrix[1], matrix[2], matrix[3], + matrix[4], matrix[5], matrix[6], matrix[7], + matrix[8], matrix[9], matrix[10], matrix[11], + matrix[12], matrix[13], matrix[14], matrix[15]]; + } + result[0] = matrix[0]; + result[1] = matrix[1]; + result[2] = matrix[2]; + result[3] = matrix[3]; + result[4] = matrix[4]; + result[5] = matrix[5]; + result[6] = matrix[6]; + result[7] = matrix[7]; + result[8] = matrix[8]; + result[9] = matrix[9]; + result[10] = matrix[10]; + result[11] = matrix[11]; + result[12] = matrix[12]; + result[13] = matrix[13]; + result[14] = matrix[14]; + result[15] = matrix[15]; + return result; + }; + + /** + * Computes the array index of the element at the provided row and column. + * @memberof Matrix4 + * + * @param {Number} row The zero-based index of the row. + * @param {Number} column The zero-based index of the column. + * @returns {Number} The index of the element at the provided row and column. + * + * @exception {DeveloperError} row is required and must be 0, 1, 2, or 3. + * @exception {DeveloperError} column is required and must be 0, 1, 2, or 3. + * + * @example + * var myMatrix = new Matrix4(); + * var column1Row0Index = Matrix4.getElementIndex(1, 0); + * var column1Row0 = myMatrix[column1Row0Index] + * myMatrix[column1Row0Index] = 10.0; + */ + Matrix4.getElementIndex = function(column, row) { + if (typeof row !== 'number' || row < 0 || row > 3) { + throw new DeveloperError('row is required and must be 0, 1, 2, or 3.'); + } + if (typeof column !== 'number' || column < 0 || column > 3) { + throw new DeveloperError('column is required and must be 0, 1, 2, or 3.'); + } + + return column * 4 + row; + }; + + /** + * Retrieves a copy of the matrix column at the provided index as a Cartesian4 instance. + * @memberof Matrix4 + * + * @param {Matrix4} matrix The matrix to use. + * @param {Number} index The zero-based index of the column to retrieve. + * @param {Cartesian4} [result] The object onto which to store the result. + * @returns {Cartesian4} The modified result parameter or a new Cartesian4 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} index is required and must be 0, 1, 2, or 3. + * + * @see Cartesian4 + * + * @example + * //returns a Cartesian4 instance with values from the specified column + * // m = [10.0, 11.0, 12.0, 13.0] + * // [14.0, 15.0, 16.0, 17.0] + * // [18.0, 19.0, 20.0, 21.0] + * // [22.0, 23.0, 24.0, 25.0] + * + * //Example 1: Creates an instance of Cartesian + * var a = Matrix4.getColumn(m, 2); + * + * //Example 2: Sets values for Cartesian instance + * var a = new Cartesian4(); + * Matrix4.getColumn(m, 2, a); + * + * // a.x = 12.0; a.y = 16.0; a.z = 20.0; a.w = 24.0; + * + */ + Matrix4.getColumn = function(matrix, index, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required.'); + } + + if (typeof index !== 'number' || index < 0 || index > 3) { + throw new DeveloperError('index is required and must be 0, 1, 2, or 3.'); + } + + var startIndex = index * 4; + var x = matrix[startIndex]; + var y = matrix[startIndex + 1]; + var z = matrix[startIndex + 2]; + var w = matrix[startIndex + 3]; + + if (!defined(result)) { + return new Cartesian4(x, y, z, w); + } + result.x = x; + result.y = y; + result.z = z; + result.w = w; + return result; + }; + + /** + * Computes a new matrix that replaces the specified column in the provided matrix with the provided Cartesian4 instance. + * @memberof Matrix4 + * + * @param {Matrix4} matrix The matrix to use. + * @param {Number} index The zero-based index of the column to set. + * @param {Cartesian4} cartesian The Cartesian whose values will be assigned to the specified column. + * @param {Cartesian4} [result] The object onto which to store the result. + * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} cartesian is required. + * @exception {DeveloperError} index is required and must be 0, 1, 2, or 3. + * + * @see Cartesian4 + * + * @example + * //creates a new Matrix4 instance with new column values from the Cartesian4 instance + * // m = [10.0, 11.0, 12.0, 13.0] + * // [14.0, 15.0, 16.0, 17.0] + * // [18.0, 19.0, 20.0, 21.0] + * // [22.0, 23.0, 24.0, 25.0] + * + * var a = Matrix4.setColumn(m, 2, new Cartesian4(99.0, 98.0, 97.0, 96.0)); + * + * // m remains the same + * // a = [10.0, 11.0, 99.0, 13.0] + * // [14.0, 15.0, 98.0, 17.0] + * // [18.0, 19.0, 97.0, 21.0] + * // [22.0, 23.0, 96.0, 25.0] + * + */ + Matrix4.setColumn = function(matrix, index, cartesian, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + if (typeof index !== 'number' || index < 0 || index > 3) { + throw new DeveloperError('index is required and must be 0, 1, 2, or 3.'); + } + + result = Matrix4.clone(matrix, result); + var startIndex = index * 4; + result[startIndex] = cartesian.x; + result[startIndex + 1] = cartesian.y; + result[startIndex + 2] = cartesian.z; + result[startIndex + 3] = cartesian.w; + return result; + }; + + /** + * Retrieves a copy of the matrix row at the provided index as a Cartesian4 instance. + * @memberof Matrix4 + * + * @param {Matrix4} matrix The matrix to use. + * @param {Number} index The zero-based index of the row to retrieve. + * @param {Cartesian4} [result] The object onto which to store the result. + * @returns {Cartesian4} The modified result parameter or a new Cartesian4 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} index is required and must be 0, 1, 2, or 3. + * + * @see Cartesian4 + * + * @example + * //returns a Cartesian4 instance with values from the specified column + * // m = [10.0, 11.0, 12.0, 13.0] + * // [14.0, 15.0, 16.0, 17.0] + * // [18.0, 19.0, 20.0, 21.0] + * // [22.0, 23.0, 24.0, 25.0] + * + * //Example 1: Returns an instance of Cartesian + * var a = Matrix4.getRow(m, 2); + * + * //Example 1: Sets values for a Cartesian instance + * var a = new Cartesian4(); + * Matrix4.getRow(m, 2, a); + * + * // a.x = 18.0; a.y = 19.0; a.z = 20.0; a.w = 21.0; + */ + Matrix4.getRow = function(matrix, index, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required.'); + } + + if (typeof index !== 'number' || index < 0 || index > 3) { + throw new DeveloperError('index is required and must be 0, 1, 2, or 3.'); + } + + var x = matrix[index]; + var y = matrix[index + 4]; + var z = matrix[index + 8]; + var w = matrix[index + 12]; + + if (!defined(result)) { + return new Cartesian4(x, y, z, w); + } + result.x = x; + result.y = y; + result.z = z; + result.w = w; + return result; + }; + + /** + * Computes a new matrix that replaces the specified row in the provided matrix with the provided Cartesian4 instance. + * @memberof Matrix4 + * + * @param {Matrix4} matrix The matrix to use. + * @param {Number} index The zero-based index of the row to set. + * @param {Cartesian4} cartesian The Cartesian whose values will be assigned to the specified row. + * @param {Cartesian4} [result] The object onto which to store the result. + * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} cartesian is required. + * @exception {DeveloperError} index is required and must be 0, 1, 2, or 3. + * + * @see Cartesian4 + * + * @example + * //create a new Matrix4 instance with new row values from the Cartesian4 instance + * // m = [10.0, 11.0, 12.0, 13.0] + * // [14.0, 15.0, 16.0, 17.0] + * // [18.0, 19.0, 20.0, 21.0] + * // [22.0, 23.0, 24.0, 25.0] + * + * var a = Matrix4.setRow(m, 2, new Cartesian4(99.0, 98.0, 97.0, 96.0)); + * + * // m remains the same + * // a = [10.0, 11.0, 12.0, 13.0] + * // [14.0, 15.0, 16.0, 17.0] + * // [99.0, 98.0, 97.0, 96.0] + * // [22.0, 23.0, 24.0, 25.0] + * + */ + Matrix4.setRow = function(matrix, index, cartesian, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + if (typeof index !== 'number' || index < 0 || index > 3) { + throw new DeveloperError('index is required and must be 0, 1, 2, or 3.'); + } + + result = Matrix4.clone(matrix, result); + result[index] = cartesian.x; + result[index + 4] = cartesian.y; + result[index + 8] = cartesian.z; + result[index + 12] = cartesian.w; + return result; + }; + + /** + * Computes the product of two matrices. + * @memberof Matrix4 + * + * @param {Matrix4} left The first matrix. + * @param {Matrix4} right The second matrix. + * @param {Matrix4} [result] The object onto which to store the result. + * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + */ + Matrix4.multiply = function(left, right, result) { + if (!defined(left)) { + throw new DeveloperError('left is required'); + } + if (!defined(right)) { + throw new DeveloperError('right is required'); + } + + var left0 = left[0]; + var left1 = left[1]; + var left2 = left[2]; + var left3 = left[3]; + var left4 = left[4]; + var left5 = left[5]; + var left6 = left[6]; + var left7 = left[7]; + var left8 = left[8]; + var left9 = left[9]; + var left10 = left[10]; + var left11 = left[11]; + var left12 = left[12]; + var left13 = left[13]; + var left14 = left[14]; + var left15 = left[15]; + + var right0 = right[0]; + var right1 = right[1]; + var right2 = right[2]; + var right3 = right[3]; + var right4 = right[4]; + var right5 = right[5]; + var right6 = right[6]; + var right7 = right[7]; + var right8 = right[8]; + var right9 = right[9]; + var right10 = right[10]; + var right11 = right[11]; + var right12 = right[12]; + var right13 = right[13]; + var right14 = right[14]; + var right15 = right[15]; + + var column0Row0 = left0 * right0 + left4 * right1 + left8 * right2 + left12 * right3; + var column0Row1 = left1 * right0 + left5 * right1 + left9 * right2 + left13 * right3; + var column0Row2 = left2 * right0 + left6 * right1 + left10 * right2 + left14 * right3; + var column0Row3 = left3 * right0 + left7 * right1 + left11 * right2 + left15 * right3; + + var column1Row0 = left0 * right4 + left4 * right5 + left8 * right6 + left12 * right7; + var column1Row1 = left1 * right4 + left5 * right5 + left9 * right6 + left13 * right7; + var column1Row2 = left2 * right4 + left6 * right5 + left10 * right6 + left14 * right7; + var column1Row3 = left3 * right4 + left7 * right5 + left11 * right6 + left15 * right7; + + var column2Row0 = left0 * right8 + left4 * right9 + left8 * right10 + left12 * right11; + var column2Row1 = left1 * right8 + left5 * right9 + left9 * right10 + left13 * right11; + var column2Row2 = left2 * right8 + left6 * right9 + left10 * right10 + left14 * right11; + var column2Row3 = left3 * right8 + left7 * right9 + left11 * right10 + left15 * right11; + + var column3Row0 = left0 * right12 + left4 * right13 + left8 * right14 + left12 * right15; + var column3Row1 = left1 * right12 + left5 * right13 + left9 * right14 + left13 * right15; + var column3Row2 = left2 * right12 + left6 * right13 + left10 * right14 + left14 * right15; + var column3Row3 = left3 * right12 + left7 * right13 + left11 * right14 + left15 * right15; + + if (!defined(result)) { + return new Matrix4(column0Row0, column1Row0, column2Row0, column3Row0, + column0Row1, column1Row1, column2Row1, column3Row1, + column0Row2, column1Row2, column2Row2, column3Row2, + column0Row3, column1Row3, column2Row3, column3Row3); + } + result[0] = column0Row0; + result[1] = column0Row1; + result[2] = column0Row2; + result[3] = column0Row3; + result[4] = column1Row0; + result[5] = column1Row1; + result[6] = column1Row2; + result[7] = column1Row3; + result[8] = column2Row0; + result[9] = column2Row1; + result[10] = column2Row2; + result[11] = column2Row3; + result[12] = column3Row0; + result[13] = column3Row1; + result[14] = column3Row2; + result[15] = column3Row3; + return result; + }; + + /** + * Computes the product of two matrices assuming the matrices are + * affine transformation matrices, where the upper left 3x3 elements + * are a rotation matrix, and the upper three elements in the fourth + * column are the translation. The bottom row is assumed to be [0, 0, 0, 1]. + * The matrix is not verified to be in the proper form. + * This method is faster than computing the product for general 4x4 + * matrices using {@link #multiply}. + * @memberof Matrix4 + * + * @param {Matrix4} left The first matrix. + * @param {Matrix4} right The second matrix. + * @param {Matrix4} [result] The object onto which to store the result. + * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + * + * @example + * var m1 = new Matrix4(1.0, 6.0, 7.0, 0.0, 2.0, 5.0, 8.0, 0.0, 3.0, 4.0, 9.0, 0.0, 0.0, 0.0, 0.0, 1.0]; + * var m2 = Transforms.eastNorthUpToFixedFrame(new Cartesian3(1.0, 1.0, 1.0)); + * var m3 = Matrix4.multiplyTransformation(m1, m2); + */ + Matrix4.multiplyTransformation = function(left, right, result) { + if (!defined(left)) { + throw new DeveloperError('left is required'); + } + if (!defined(right)) { + throw new DeveloperError('right is required'); + } + + var left0 = left[0]; + var left1 = left[1]; + var left2 = left[2]; + var left4 = left[4]; + var left5 = left[5]; + var left6 = left[6]; + var left8 = left[8]; + var left9 = left[9]; + var left10 = left[10]; + var left12 = left[12]; + var left13 = left[13]; + var left14 = left[14]; + + var right0 = right[0]; + var right1 = right[1]; + var right2 = right[2]; + var right4 = right[4]; + var right5 = right[5]; + var right6 = right[6]; + var right8 = right[8]; + var right9 = right[9]; + var right10 = right[10]; + var right12 = right[12]; + var right13 = right[13]; + var right14 = right[14]; + + var column0Row0 = left0 * right0 + left4 * right1 + left8 * right2; + var column0Row1 = left1 * right0 + left5 * right1 + left9 * right2; + var column0Row2 = left2 * right0 + left6 * right1 + left10 * right2; + + var column1Row0 = left0 * right4 + left4 * right5 + left8 * right6; + var column1Row1 = left1 * right4 + left5 * right5 + left9 * right6; + var column1Row2 = left2 * right4 + left6 * right5 + left10 * right6; + + var column2Row0 = left0 * right8 + left4 * right9 + left8 * right10; + var column2Row1 = left1 * right8 + left5 * right9 + left9 * right10; + var column2Row2 = left2 * right8 + left6 * right9 + left10 * right10; + + var column3Row0 = left0 * right12 + left4 * right13 + left8 * right14 + left12; + var column3Row1 = left1 * right12 + left5 * right13 + left9 * right14 + left13; + var column3Row2 = left2 * right12 + left6 * right13 + left10 * right14 + left14; + + if (!defined(result)) { + return new Matrix4(column0Row0, column1Row0, column2Row0, column3Row0, + column0Row1, column1Row1, column2Row1, column3Row1, + column0Row2, column1Row2, column2Row2, column3Row2, + 0.0, 0.0, 0.0, 1.0); + } + result[0] = column0Row0; + result[1] = column0Row1; + result[2] = column0Row2; + result[3] = 0.0; + result[4] = column1Row0; + result[5] = column1Row1; + result[6] = column1Row2; + result[7] = 0.0; + result[8] = column2Row0; + result[9] = column2Row1; + result[10] = column2Row2; + result[11] = 0.0; + result[12] = column3Row0; + result[13] = column3Row1; + result[14] = column3Row2; + result[15] = 1.0; + return result; + }; + + /** + * Multiplies a transformation matrix (with a bottom row of <code>[0.0, 0.0, 0.0, 1.0]</code>) + * by an implicit translation matrix defined by a {@link Cartesian3}. This is an optimization + * for <code>Matrix4.multiply(m, Matrix4.fromTranslation(position), m);</code> with less allocations and arithmetic operations. + * + * @memberof Matrix4 + * + * @param {Matrix4} matrix The matrix on the left-hand side. + * @param {Cartesian3} translation The translation on the right-hand side. + * @param {Matrix4} [result] The object onto which to store the result. + * + * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} translation is required. + * + * @see Matrix4#fromTranslation + * + * @example + * // Instead of Matrix4.multiply(m, Matrix4.fromTranslation(position), m); + * Matrix4.multiplyByTranslation(m, position, m); + */ + Matrix4.multiplyByTranslation = function(matrix, translation, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + if (!defined(translation)) { + throw new DeveloperError('translation is required'); + } + + var x = translation.x; + var y = translation.y; + var z = translation.z; + + var tx = (x * matrix[0]) + (y * matrix[4]) + (z * matrix[8]) + matrix[12]; + var ty = (x * matrix[1]) + (y * matrix[5]) + (z * matrix[9]) + matrix[13]; + var tz = (x * matrix[2]) + (y * matrix[6]) + (z * matrix[10]) + matrix[14]; + + if (!defined(result)) { + return new Matrix4(matrix[0], matrix[4], matrix[8], tx, + matrix[1], matrix[5], matrix[9], ty, + matrix[2], matrix[6], matrix[10], tz, + matrix[3], matrix[7], matrix[11], matrix[15]); + } + + result[0] = matrix[0]; + result[1] = matrix[1]; + result[2] = matrix[2]; + result[3] = matrix[3]; + result[4] = matrix[4]; + result[5] = matrix[5]; + result[6] = matrix[6]; + result[7] = matrix[7]; + result[8] = matrix[8]; + result[9] = matrix[9]; + result[10] = matrix[10]; + result[11] = matrix[11]; + result[12] = tx; + result[13] = ty; + result[14] = tz; + result[15] = matrix[15]; + return result; + }; + + var uniformScaleScratch = new Cartesian3(); + + /** + * Multiplies a transformation matrix (with a bottom row of <code>[0.0, 0.0, 0.0, 1.0]</code>) + * by an implicit uniform scale matrix. This is an optimization + * for <code>Matrix4.multiply(m, Matrix4.fromUniformScale(scale), m);</code> with less allocations and arithmetic operations. + * + * @memberof Matrix4 + * + * @param {Matrix4} matrix The matrix on the left-hand side. + * @param {Number} scale The uniform scale on the right-hand side. + * @param {Matrix4} [result] The object onto which to store the result. + * + * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} scale is required. + * + * @see Matrix4#fromUniformScale + * @see Matrix4#multiplyByScale + * + * @example + * // Instead of Matrix4.multiply(m, Matrix4.fromUniformScale(scale), m); + * Matrix4.multiplyByUniformScale(m, scale, m); + */ + Matrix4.multiplyByUniformScale = function(matrix, scale, result) { + if (typeof scale !== 'number') { + throw new DeveloperError('scale is required'); + } + + uniformScaleScratch.x = scale; + uniformScaleScratch.y = scale; + uniformScaleScratch.z = scale; + return Matrix4.multiplyByScale(matrix, uniformScaleScratch, result); + }; + + /** + * Multiplies a transformation matrix (with a bottom row of <code>[0.0, 0.0, 0.0, 1.0]</code>) + * by an implicit non-uniform scale matrix. This is an optimization + * for <code>Matrix4.multiply(m, Matrix4.fromScale(scale), m);</code> with less allocations and arithmetic operations. + * + * @memberof Matrix4 + * + * @param {Matrix4} matrix The matrix on the left-hand side. + * @param {Cartesian3} scale The non-uniform scale on the right-hand side. + * @param {Matrix4} [result] The object onto which to store the result. + * + * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} scale is required. + * + * @see Matrix4#fromScale + * @see Matrix4#multiplyByUniformScale + * + * @example + * // Instead of Matrix4.multiply(m, Matrix4.fromScale(scale), m); + * Matrix4.multiplyByUniformScale(m, scale, m); + */ + Matrix4.multiplyByScale = function(matrix, scale, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + if (!defined(scale)) { + throw new DeveloperError('scale is required'); + } + + var scaleX = scale.x; + var scaleY = scale.y; + var scaleZ = scale.z; + + // Faster than Cartesian3.equals + if ((scaleX === 1.0) && (scaleY === 1.0) && (scaleZ === 1.0)) { + return Matrix4.clone(matrix, result); + } + + if (!defined(result)) { + return new Matrix4( + scaleX * matrix[0], scaleY * matrix[4], scaleZ * matrix[8], matrix[12], + scaleX * matrix[1], scaleY * matrix[5], scaleZ * matrix[9], matrix[13], + scaleX * matrix[2], scaleY * matrix[6], scaleZ * matrix[10], matrix[14], + 0.0, 0.0, 0.0, 1.0); + } + + result[0] = scaleX * matrix[0]; + result[1] = scaleX * matrix[1]; + result[2] = scaleX * matrix[2]; + result[3] = 0.0; + result[4] = scaleY * matrix[4]; + result[5] = scaleY * matrix[5]; + result[6] = scaleY * matrix[6]; + result[7] = 0.0; + result[8] = scaleZ * matrix[8]; + result[9] = scaleZ * matrix[9]; + result[10] = scaleZ * matrix[10]; + result[11] = 0.0; + result[12] = matrix[12]; + result[13] = matrix[13]; + result[14] = matrix[14]; + result[15] = 1.0; + return result; + }; + + /** + * Computes the product of a matrix and a column vector. + * @memberof Matrix4 + * + * @param {Matrix4} matrix The matrix. + * @param {Cartesian4} cartesian The vector. + * @param {Cartesian4} [result] The object onto which to store the result. + * @returns {Cartesian4} The modified result parameter or a new Cartesian4 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} cartesian is required. + */ + Matrix4.multiplyByVector = function(matrix, cartesian, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + var vX = cartesian.x; + var vY = cartesian.y; + var vZ = cartesian.z; + var vW = cartesian.w; + + var x = matrix[0] * vX + matrix[4] * vY + matrix[8] * vZ + matrix[12] * vW; + var y = matrix[1] * vX + matrix[5] * vY + matrix[9] * vZ + matrix[13] * vW; + var z = matrix[2] * vX + matrix[6] * vY + matrix[10] * vZ + matrix[14] * vW; + var w = matrix[3] * vX + matrix[7] * vY + matrix[11] * vZ + matrix[15] * vW; + + if (!defined(result)) { + return new Cartesian4(x, y, z, w); + } + result.x = x; + result.y = y; + result.z = z; + result.w = w; + return result; + }; + + /** + * Computes the product of a matrix and a {@link Cartesian3}. This is equivalent to calling {@link Matrix4.multiplyByVector} + * with a {@link Cartesian4} with a <code>w</code> component of zero. + * @memberof Matrix4 + * + * @param {Matrix4} matrix The matrix. + * @param {Cartesian3} cartesian The point. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + * @exception {DeveloperError} matrix is required. + * + * @example + * Cartesian3 p = new Cartesian3(1.0, 2.0, 3.0); + * Matrix4.multiplyByPointAsVector(matrix, p, result); + * // A shortcut for + * // Cartesian3 p = ... + * // Matrix4.multiplyByVector(matrix, new Cartesian4(p.x, p.y, p.z, 0.0), result); + */ + Matrix4.multiplyByPointAsVector = function(matrix, cartesian, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + var vX = cartesian.x; + var vY = cartesian.y; + var vZ = cartesian.z; + + var x = matrix[0] * vX + matrix[4] * vY + matrix[8] * vZ; + var y = matrix[1] * vX + matrix[5] * vY + matrix[9] * vZ; + var z = matrix[2] * vX + matrix[6] * vY + matrix[10] * vZ; + + if (!defined(result)) { + return new Cartesian3(x, y, z); + } + result.x = x; + result.y = y; + result.z = z; + return result; + }; + + /** + * Computes the product of a matrix and a {@link Cartesian3}. This is equivalent to calling {@link Matrix4.multiplyByVector} + * with a {@link Cartesian4} with a <code>w</code> component of 1, but returns a {@link Cartesian3} instead of a {@link Cartesian4}. + * @memberof Matrix4 + * + * @param {Matrix4} matrix The matrix. + * @param {Cartesian3} cartesian The point. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + * @exception {DeveloperError} matrix is required. + * + * @example + * Cartesian3 p = new Cartesian3(1.0, 2.0, 3.0); + * Matrix4.multiplyByPoint(matrix, p, result); + */ + Matrix4.multiplyByPoint = function(matrix, cartesian, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + var vX = cartesian.x; + var vY = cartesian.y; + var vZ = cartesian.z; + + var x = matrix[0] * vX + matrix[4] * vY + matrix[8] * vZ + matrix[12]; + var y = matrix[1] * vX + matrix[5] * vY + matrix[9] * vZ + matrix[13]; + var z = matrix[2] * vX + matrix[6] * vY + matrix[10] * vZ + matrix[14]; + + if (!defined(result)) { + return new Cartesian3(x, y, z); + } + result.x = x; + result.y = y; + result.z = z; + return result; + }; + + /** + * Computes the product of a matrix and a scalar. + * @memberof Matrix4 + * + * @param {Matrix4} matrix The matrix. + * @param {Number} scalar The number to multiply by. + * @param {Matrix4} [result] The object onto which to store the result. + * @returns {Matrix4} The modified result parameter or a new Cartesian4 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} scalar is required and must be a number. + * + * @example + * //create a Matrix4 instance which is a scaled version of the supplied Matrix4 + * // m = [10.0, 11.0, 12.0, 13.0] + * // [14.0, 15.0, 16.0, 17.0] + * // [18.0, 19.0, 20.0, 21.0] + * // [22.0, 23.0, 24.0, 25.0] + * + * var a = Matrix4.multiplyByScalar(m, -2); + * + * // m remains the same + * // a = [-20.0, -22.0, -24.0, -26.0] + * // [-28.0, -30.0, -32.0, -34.0] + * // [-36.0, -38.0, -40.0, -42.0] + * // [-44.0, -46.0, -48.0, -50.0] + * + */ + Matrix4.multiplyByScalar = function(matrix, scalar, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + if (typeof scalar !== 'number') { + throw new DeveloperError('scalar is required and must be a number'); + } + + if (!defined(result)) { + return new Matrix4(matrix[0] * scalar, matrix[4] * scalar, matrix[8] * scalar, matrix[12] * scalar, + matrix[1] * scalar, matrix[5] * scalar, matrix[9] * scalar, matrix[13] * scalar, + matrix[2] * scalar, matrix[6] * scalar, matrix[10] * scalar, matrix[14] * scalar, + matrix[3] * scalar, matrix[7] * scalar, matrix[11] * scalar, matrix[15] * scalar); + } + result[0] = matrix[0] * scalar; + result[1] = matrix[1] * scalar; + result[2] = matrix[2] * scalar; + result[3] = matrix[3] * scalar; + result[4] = matrix[4] * scalar; + result[5] = matrix[5] * scalar; + result[6] = matrix[6] * scalar; + result[7] = matrix[7] * scalar; + result[8] = matrix[8] * scalar; + result[9] = matrix[9] * scalar; + result[10] = matrix[10] * scalar; + result[11] = matrix[11] * scalar; + result[12] = matrix[12] * scalar; + result[13] = matrix[13] * scalar; + result[14] = matrix[14] * scalar; + result[15] = matrix[15] * scalar; + return result; + }; + + /** + * Computes a negated copy of the provided matrix. + * @memberof Matrix4 + * + * @param {Matrix4} matrix The matrix to negate. + * @param {Matrix4} [result] The object onto which to store the result. + * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * + * @example + * //create a new Matrix4 instance which is a negation of a Matrix4 + * // m = [10.0, 11.0, 12.0, 13.0] + * // [14.0, 15.0, 16.0, 17.0] + * // [18.0, 19.0, 20.0, 21.0] + * // [22.0, 23.0, 24.0, 25.0] + * + * var a = Matrix4.negate(m); + * + * // m remains the same + * // a = [-10.0, -11.0, -12.0, -13.0] + * // [-14.0, -15.0, -16.0, -17.0] + * // [-18.0, -19.0, -20.0, -21.0] + * // [-22.0, -23.0, -24.0, -25.0] + * + */ + Matrix4.negate = function(matrix, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + + if (!defined(result)) { + return new Matrix4(-matrix[0], -matrix[4], -matrix[8], -matrix[12], + -matrix[1], -matrix[5], -matrix[9], -matrix[13], + -matrix[2], -matrix[6], -matrix[10], -matrix[14], + -matrix[3], -matrix[7], -matrix[11], -matrix[15]); + } + result[0] = -matrix[0]; + result[1] = -matrix[1]; + result[2] = -matrix[2]; + result[3] = -matrix[3]; + result[4] = -matrix[4]; + result[5] = -matrix[5]; + result[6] = -matrix[6]; + result[7] = -matrix[7]; + result[8] = -matrix[8]; + result[9] = -matrix[9]; + result[10] = -matrix[10]; + result[11] = -matrix[11]; + result[12] = -matrix[12]; + result[13] = -matrix[13]; + result[14] = -matrix[14]; + result[15] = -matrix[15]; + return result; + }; + + /** + * Computes the transpose of the provided matrix. + * @memberof Matrix4 + * + * @param {Matrix4} matrix The matrix to transpose. + * @param {Matrix4} [result] The object onto which to store the result. + * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * + * @example + * //returns transpose of a Matrix4 + * // m = [10.0, 11.0, 12.0, 13.0] + * // [14.0, 15.0, 16.0, 17.0] + * // [18.0, 19.0, 20.0, 21.0] + * // [22.0, 23.0, 24.0, 25.0] + * + * var a = Matrix4.negate(m); + * + * // m remains the same + * // a = [10.0, 14.0, 18.0, 22.0] + * // [11.0, 15.0, 19.0, 23.0] + * // [12.0, 16.0, 20.0, 24.0] + * // [13.0, 17.0, 21.0, 25.0] + * + */ + Matrix4.transpose = function(matrix, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + + if (!defined(result)) { + return new Matrix4(matrix[0], matrix[1], matrix[2], matrix[3], + matrix[4], matrix[5], matrix[6], matrix[7], + matrix[8], matrix[9], matrix[10], matrix[11], + matrix[12], matrix[13], matrix[14], matrix[15]); + } + + var matrix1 = matrix[1]; + var matrix2 = matrix[2]; + var matrix3 = matrix[3]; + var matrix6 = matrix[6]; + var matrix7 = matrix[7]; + var matrix11 = matrix[11]; + + result[0] = matrix[0]; + result[1] = matrix[4]; + result[2] = matrix[8]; + result[3] = matrix[12]; + result[4] = matrix1; + result[5] = matrix[5]; + result[6] = matrix[9]; + result[7] = matrix[13]; + result[8] = matrix2; + result[9] = matrix6; + result[10] = matrix[10]; + result[11] = matrix[14]; + result[12] = matrix3; + result[13] = matrix7; + result[14] = matrix11; + result[15] = matrix[15]; + return result; + }; + + /** + * Computes a matrix, which contains the absolute (unsigned) values of the provided matrix's elements. + * @memberof Matrix4 + * + * @param {Matrix4} matrix The matrix with signed elements. + * @param {Matrix4} [result] The object onto which to store the result. + * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + */ + Matrix4.abs = function(matrix, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + + if (!defined(result)) { + return new Matrix4(Math.abs(matrix[0]), Math.abs(matrix[4]), Math.abs(matrix[8]), Math.abs(matrix[12]), + Math.abs(matrix[1]), Math.abs(matrix[5]), Math.abs(matrix[9]), Math.abs(matrix[13]), + Math.abs(matrix[2]), Math.abs(matrix[6]), Math.abs(matrix[10]), Math.abs(matrix[14]), + Math.abs(matrix[3]), Math.abs(matrix[7]), Math.abs(matrix[11]), Math.abs(matrix[15])); + + } + + result[0] = Math.abs(matrix[0]); + result[1] = Math.abs(matrix[1]); + result[2] = Math.abs(matrix[2]); + result[3] = Math.abs(matrix[3]); + result[4] = Math.abs(matrix[4]); + result[5] = Math.abs(matrix[5]); + result[6] = Math.abs(matrix[6]); + result[7] = Math.abs(matrix[7]); + result[8] = Math.abs(matrix[8]); + result[9] = Math.abs(matrix[9]); + result[10] = Math.abs(matrix[10]); + result[11] = Math.abs(matrix[11]); + result[12] = Math.abs(matrix[12]); + result[13] = Math.abs(matrix[13]); + result[14] = Math.abs(matrix[14]); + result[15] = Math.abs(matrix[15]); + + return result; + }; + + /** + * Compares the provided matrices componentwise and returns + * <code>true</code> if they are equal, <code>false</code> otherwise. + * @memberof Matrix4 + * + * @param {Matrix4} [left] The first matrix. + * @param {Matrix4} [right] The second matrix. + * @returns {Boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise. + * + * @example + * //compares two Matrix4 instances + * + * // a = [10.0, 14.0, 18.0, 22.0] + * // [11.0, 15.0, 19.0, 23.0] + * // [12.0, 16.0, 20.0, 24.0] + * // [13.0, 17.0, 21.0, 25.0] + * + * // b = [10.0, 14.0, 18.0, 22.0] + * // [11.0, 15.0, 19.0, 23.0] + * // [12.0, 16.0, 20.0, 24.0] + * // [13.0, 17.0, 21.0, 25.0] + * + * if(Matrix4.equals(a,b)) { + * console.log("Both matrices are equal"); + * } else { + * console.log("They are not equal"); + * } + * + * //Prints "Both matrices are equal" on the console + * + */ + Matrix4.equals = function(left, right) { + return (left === right) || + (defined(left) && + defined(right) && + left[0] === right[0] && + left[1] === right[1] && + left[2] === right[2] && + left[3] === right[3] && + left[4] === right[4] && + left[5] === right[5] && + left[6] === right[6] && + left[7] === right[7] && + left[8] === right[8] && + left[9] === right[9] && + left[10] === right[10] && + left[11] === right[11] && + left[12] === right[12] && + left[13] === right[13] && + left[14] === right[14] && + left[15] === right[15]); + }; + + /** + * Compares the provided matrices componentwise and returns + * <code>true</code> if they are within the provided epsilon, + * <code>false</code> otherwise. + * @memberof Matrix4 + * + * @param {Matrix4} [left] The first matrix. + * @param {Matrix4} [right] The second matrix. + * @param {Number} epsilon The epsilon to use for equality testing. + * @returns {Boolean} <code>true</code> if left and right are within the provided epsilon, <code>false</code> otherwise. + * + * @exception {DeveloperError} epsilon is required and must be a number. + * + * @example + * //compares two Matrix4 instances + * + * // a = [10.5, 14.5, 18.5, 22.5] + * // [11.5, 15.5, 19.5, 23.5] + * // [12.5, 16.5, 20.5, 24.5] + * // [13.5, 17.5, 21.5, 25.5] + * + * // b = [10.0, 14.0, 18.0, 22.0] + * // [11.0, 15.0, 19.0, 23.0] + * // [12.0, 16.0, 20.0, 24.0] + * // [13.0, 17.0, 21.0, 25.0] + * + * if(Matrix4.equalsEpsilon(a,b,0.1)){ + * console.log("Difference between both the matrices is less than 0.1"); + * } else { + * console.log("Difference between both the matrices is not less than 0.1"); + * } + * + * //Prints "Difference between both the matrices is not less than 0.1" on the console + * + */ + Matrix4.equalsEpsilon = function(left, right, epsilon) { + if (typeof epsilon !== 'number') { + throw new DeveloperError('epsilon is required and must be a number'); + } + + return (left === right) || + (defined(left) && + defined(right) && + Math.abs(left[0] - right[0]) <= epsilon && + Math.abs(left[1] - right[1]) <= epsilon && + Math.abs(left[2] - right[2]) <= epsilon && + Math.abs(left[3] - right[3]) <= epsilon && + Math.abs(left[4] - right[4]) <= epsilon && + Math.abs(left[5] - right[5]) <= epsilon && + Math.abs(left[6] - right[6]) <= epsilon && + Math.abs(left[7] - right[7]) <= epsilon && + Math.abs(left[8] - right[8]) <= epsilon && + Math.abs(left[9] - right[9]) <= epsilon && + Math.abs(left[10] - right[10]) <= epsilon && + Math.abs(left[11] - right[11]) <= epsilon && + Math.abs(left[12] - right[12]) <= epsilon && + Math.abs(left[13] - right[13]) <= epsilon && + Math.abs(left[14] - right[14]) <= epsilon && + Math.abs(left[15] - right[15]) <= epsilon); + }; + + /** + * Gets the translation portion of the provided matrix, assuming the matrix is a affine transformation matrix. + * @memberof Matrix4 + * + * @param {Matrix4} matrix The matrix to use. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * + * @see Cartesian3 + */ + Matrix4.getTranslation = function(matrix, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + + if (!defined(result)) { + return new Cartesian3(matrix[12], matrix[13], matrix[14]); + } + result.x = matrix[12]; + result.y = matrix[13]; + result.z = matrix[14]; + return result; + }; + + /** + * Gets the upper left 3x3 rotation matrix of the provided matrix, assuming the matrix is a affine transformation matrix. + * @memberof Matrix4 + * + * @param {Matrix4} matrix The matrix to use. + * @param {Matrix3} [result] The object onto which to store the result. + * @returns {Matrix3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * + * @see Matrix3 + * + * @example + * // returns a Matrix3 instance from a Matrix4 instance + * + * // m = [10.0, 14.0, 18.0, 22.0] + * // [11.0, 15.0, 19.0, 23.0] + * // [12.0, 16.0, 20.0, 24.0] + * // [13.0, 17.0, 21.0, 25.0] + * + * var b = new Matrix3(); + * Matrix4.getRotation(m,b); + * + * // b = [10.0, 14.0, 18.0] + * // [11.0, 15.0, 19.0] + * // [12.0, 16.0, 20.0] + * + */ + Matrix4.getRotation = function(matrix, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + + if (!defined(result)) { + return new Matrix3(matrix[0], matrix[4], matrix[8], + matrix[1], matrix[5], matrix[9], + matrix[2], matrix[6], matrix[10]); + } + result[0] = matrix[0]; + result[1] = matrix[1]; + result[2] = matrix[2]; + result[3] = matrix[4]; + result[4] = matrix[5]; + result[5] = matrix[6]; + result[6] = matrix[8]; + result[7] = matrix[9]; + result[8] = matrix[10]; + return result; + }; + + /** + * Computes the inverse of the provided matrix using Cramers Rule. + * If the determinant is zero, the matrix can not be inverted, and an exception is thrown. + * If the matrix is an affine transformation matrix, it is more efficient + * to invert it with {@link #inverseTransformation}. + * @memberof Matrix4 + * + * @param {Matrix4} matrix The matrix to invert. + * @param {Matrix4} [result] The object onto which to store the result. + * @returns {Matrix4} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {RuntimeError} matrix is not invertible because its determinate is zero. + */ + Matrix4.inverse = function(matrix, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + + // + // Ported from: + // ftp://download.intel.com/design/PentiumIII/sml/24504301.pdf + // + var src0 = matrix[0]; + var src1 = matrix[4]; + var src2 = matrix[8]; + var src3 = matrix[12]; + var src4 = matrix[1]; + var src5 = matrix[5]; + var src6 = matrix[9]; + var src7 = matrix[13]; + var src8 = matrix[2]; + var src9 = matrix[6]; + var src10 = matrix[10]; + var src11 = matrix[14]; + var src12 = matrix[3]; + var src13 = matrix[7]; + var src14 = matrix[11]; + var src15 = matrix[15]; + + // calculate pairs for first 8 elements (cofactors) + var tmp0 = src10 * src15; + var tmp1 = src11 * src14; + var tmp2 = src9 * src15; + var tmp3 = src11 * src13; + var tmp4 = src9 * src14; + var tmp5 = src10 * src13; + var tmp6 = src8 * src15; + var tmp7 = src11 * src12; + var tmp8 = src8 * src14; + var tmp9 = src10 * src12; + var tmp10 = src8 * src13; + var tmp11 = src9 * src12; + + // calculate first 8 elements (cofactors) + var dst0 = (tmp0 * src5 + tmp3 * src6 + tmp4 * src7) - (tmp1 * src5 + tmp2 * src6 + tmp5 * src7); + var dst1 = (tmp1 * src4 + tmp6 * src6 + tmp9 * src7) - (tmp0 * src4 + tmp7 * src6 + tmp8 * src7); + var dst2 = (tmp2 * src4 + tmp7 * src5 + tmp10 * src7) - (tmp3 * src4 + tmp6 * src5 + tmp11 * src7); + var dst3 = (tmp5 * src4 + tmp8 * src5 + tmp11 * src6) - (tmp4 * src4 + tmp9 * src5 + tmp10 * src6); + var dst4 = (tmp1 * src1 + tmp2 * src2 + tmp5 * src3) - (tmp0 * src1 + tmp3 * src2 + tmp4 * src3); + var dst5 = (tmp0 * src0 + tmp7 * src2 + tmp8 * src3) - (tmp1 * src0 + tmp6 * src2 + tmp9 * src3); + var dst6 = (tmp3 * src0 + tmp6 * src1 + tmp11 * src3) - (tmp2 * src0 + tmp7 * src1 + tmp10 * src3); + var dst7 = (tmp4 * src0 + tmp9 * src1 + tmp10 * src2) - (tmp5 * src0 + tmp8 * src1 + tmp11 * src2); + + // calculate pairs for second 8 elements (cofactors) + tmp0 = src2 * src7; + tmp1 = src3 * src6; + tmp2 = src1 * src7; + tmp3 = src3 * src5; + tmp4 = src1 * src6; + tmp5 = src2 * src5; + tmp6 = src0 * src7; + tmp7 = src3 * src4; + tmp8 = src0 * src6; + tmp9 = src2 * src4; + tmp10 = src0 * src5; + tmp11 = src1 * src4; + + // calculate second 8 elements (cofactors) + var dst8 = (tmp0 * src13 + tmp3 * src14 + tmp4 * src15) - (tmp1 * src13 + tmp2 * src14 + tmp5 * src15); + var dst9 = (tmp1 * src12 + tmp6 * src14 + tmp9 * src15) - (tmp0 * src12 + tmp7 * src14 + tmp8 * src15); + var dst10 = (tmp2 * src12 + tmp7 * src13 + tmp10 * src15) - (tmp3 * src12 + tmp6 * src13 + tmp11 * src15); + var dst11 = (tmp5 * src12 + tmp8 * src13 + tmp11 * src14) - (tmp4 * src12 + tmp9 * src13 + tmp10 * src14); + var dst12 = (tmp2 * src10 + tmp5 * src11 + tmp1 * src9) - (tmp4 * src11 + tmp0 * src9 + tmp3 * src10); + var dst13 = (tmp8 * src11 + tmp0 * src8 + tmp7 * src10) - (tmp6 * src10 + tmp9 * src11 + tmp1 * src8); + var dst14 = (tmp6 * src9 + tmp11 * src11 + tmp3 * src8) - (tmp10 * src11 + tmp2 * src8 + tmp7 * src9); + var dst15 = (tmp10 * src10 + tmp4 * src8 + tmp9 * src9) - (tmp8 * src9 + tmp11 * src10 + tmp5 * src8); + + // calculate determinant + var det = src0 * dst0 + src1 * dst1 + src2 * dst2 + src3 * dst3; + + if (Math.abs(det) < CesiumMath.EPSILON20) { + throw new RuntimeError('matrix is not invertible because its determinate is zero.'); + } + + // calculate matrix inverse + det = 1.0 / det; + if (!defined(result)) { + return new Matrix4(dst0 * det, dst4 * det, dst8 * det, dst12 * det, + dst1 * det, dst5 * det, dst9 * det, dst13 * det, + dst2 * det, dst6 * det, dst10 * det, dst14 * det, + dst3 * det, dst7 * det, dst11 * det, dst15 * det); + } + + result[0] = dst0 * det; + result[1] = dst1 * det; + result[2] = dst2 * det; + result[3] = dst3 * det; + result[4] = dst4 * det; + result[5] = dst5 * det; + result[6] = dst6 * det; + result[7] = dst7 * det; + result[8] = dst8 * det; + result[9] = dst9 * det; + result[10] = dst10 * det; + result[11] = dst11 * det; + result[12] = dst12 * det; + result[13] = dst13 * det; + result[14] = dst14 * det; + result[15] = dst15 * det; + return result; + }; + + /** + * Computes the inverse of the provided matrix assuming it is + * an affine transformation matrix, where the upper left 3x3 elements + * are a rotation matrix, and the upper three elements in the fourth + * column are the translation. The bottom row is assumed to be [0, 0, 0, 1]. + * The matrix is not verified to be in the proper form. + * This method is faster than computing the inverse for a general 4x4 + * matrix using {@link #inverse}. + * @memberof Matrix4 + * + * @param {Matrix4} matrix The matrix to invert. + * @param {Matrix4} [result] The object onto which to store the result. + * @returns {Matrix4} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + */ + Matrix4.inverseTransformation = function(matrix, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + + //This function is an optimized version of the below 4 lines. + //var rT = Matrix3.transpose(Matrix4.getRotation(matrix)); + //var rTN = Matrix3.negate(rT); + //var rTT = Matrix3.multiplyByVector(rTN, Matrix4.getTranslation(matrix)); + //return Matrix4.fromRotationTranslation(rT, rTT, result); + + var matrix0 = matrix[0]; + var matrix1 = matrix[1]; + var matrix2 = matrix[2]; + var matrix4 = matrix[4]; + var matrix5 = matrix[5]; + var matrix6 = matrix[6]; + var matrix8 = matrix[8]; + var matrix9 = matrix[9]; + var matrix10 = matrix[10]; + + var vX = matrix[12]; + var vY = matrix[13]; + var vZ = matrix[14]; + + var x = -matrix0 * vX - matrix1 * vY - matrix2 * vZ; + var y = -matrix4 * vX - matrix5 * vY - matrix6 * vZ; + var z = -matrix8 * vX - matrix9 * vY - matrix10 * vZ; + + if (!defined(result)) { + return new Matrix4(matrix0, matrix1, matrix2, x, + matrix4, matrix5, matrix6, y, + matrix8, matrix9, matrix10, z, + 0.0, 0.0, 0.0, 1.0); + } + result[0] = matrix0; + result[1] = matrix4; + result[2] = matrix8; + result[3] = 0.0; + result[4] = matrix1; + result[5] = matrix5; + result[6] = matrix9; + result[7] = 0.0; + result[8] = matrix2; + result[9] = matrix6; + result[10] = matrix10; + result[11] = 0.0; + result[12] = x; + result[13] = y; + result[14] = z; + result[15] = 1.0; + return result; + }; + + /** + * An immutable Matrix4 instance initialized to the identity matrix. + * @memberof Matrix4 + */ + Matrix4.IDENTITY = freezeObject(new Matrix4(1.0, 0.0, 0.0, 0.0, + 0.0, 1.0, 0.0, 0.0, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0)); + + /** + * The index into Matrix4 for column 0, row 0. + * @memberof Matrix4 + */ + Matrix4.COLUMN0ROW0 = 0; + + /** + * The index into Matrix4 for column 0, row 1. + * @memberof Matrix4 + */ + Matrix4.COLUMN0ROW1 = 1; + + /** + * The index into Matrix4 for column 0, row 2. + * @memberof Matrix4 + */ + Matrix4.COLUMN0ROW2 = 2; + + /** + * The index into Matrix4 for column 0, row 3. + * @memberof Matrix4 + */ + Matrix4.COLUMN0ROW3 = 3; + + /** + * The index into Matrix4 for column 1, row 0. + * @memberof Matrix4 + */ + Matrix4.COLUMN1ROW0 = 4; + + /** + * The index into Matrix4 for column 1, row 1. + * @memberof Matrix4 + */ + Matrix4.COLUMN1ROW1 = 5; + + /** + * The index into Matrix4 for column 1, row 2. + * @memberof Matrix4 + */ + Matrix4.COLUMN1ROW2 = 6; + + /** + * The index into Matrix4 for column 1, row 3. + * @memberof Matrix4 + */ + Matrix4.COLUMN1ROW3 = 7; + + /** + * The index into Matrix4 for column 2, row 0. + * @memberof Matrix4 + */ + Matrix4.COLUMN2ROW0 = 8; + + /** + * The index into Matrix4 for column 2, row 1. + * @memberof Matrix4 + */ + Matrix4.COLUMN2ROW1 = 9; + + /** + * The index into Matrix4 for column 2, row 2. + * @memberof Matrix4 + */ + Matrix4.COLUMN2ROW2 = 10; + + /** + * The index into Matrix4 for column 2, row 3. + * @memberof Matrix4 + */ + Matrix4.COLUMN2ROW3 = 11; + + /** + * The index into Matrix4 for column 3, row 0. + * @memberof Matrix4 + */ + Matrix4.COLUMN3ROW0 = 12; + + /** + * The index into Matrix4 for column 3, row 1. + * @memberof Matrix4 + */ + Matrix4.COLUMN3ROW1 = 13; + + /** + * The index into Matrix4 for column 3, row 2. + * @memberof Matrix4 + */ + Matrix4.COLUMN3ROW2 = 14; + + /** + * The index into Matrix4 for column 3, row 3. + * @memberof Matrix4 + */ + Matrix4.COLUMN3ROW3 = 15; + + /** + * Duplicates the provided Matrix4 instance. + * @memberof Matrix4 + * + * @param {Matrix4} [result] The object onto which to store the result. + * @returns {Matrix4} The modified result parameter or a new Matrix4 instance if one was not provided. + */ + Matrix4.prototype.clone = function(result) { + return Matrix4.clone(this, result); + }; + + /** + * Compares this matrix to the provided matrix componentwise and returns + * <code>true</code> if they are equal, <code>false</code> otherwise. + * @memberof Matrix4 + * + * @param {Matrix4} [right] The right hand side matrix. + * @returns {Boolean} <code>true</code> if they are equal, <code>false</code> otherwise. + */ + Matrix4.prototype.equals = function(right) { + return Matrix4.equals(this, right); + }; + + /** + * Compares this matrix to the provided matrix componentwise and returns + * <code>true</code> if they are within the provided epsilon, + * <code>false</code> otherwise. + * @memberof Matrix4 + * + * @param {Matrix4} [right] The right hand side matrix. + * @param {Number} epsilon The epsilon to use for equality testing. + * @returns {Boolean} <code>true</code> if they are within the provided epsilon, <code>false</code> otherwise. + * + * @exception {DeveloperError} epsilon is required and must be a number. + */ + Matrix4.prototype.equalsEpsilon = function(right, epsilon) { + return Matrix4.equalsEpsilon(this, right, epsilon); + }; + + /** + * Computes a string representing this Matrix with each row being + * on a separate line and in the format '(column0, column1, column2, column3)'. + * @memberof Matrix4 + * + * @returns {String} A string representing the provided Matrix with each row being on a separate line and in the format '(column0, column1, column2, column3)'. + */ + Matrix4.prototype.toString = function() { + return '(' + this[0] + ', ' + this[4] + ', ' + this[8] + ', ' + this[12] +')\n' + + '(' + this[1] + ', ' + this[5] + ', ' + this[9] + ', ' + this[13] +')\n' + + '(' + this[2] + ', ' + this[6] + ', ' + this[10] + ', ' + this[14] +')\n' + + '(' + this[3] + ', ' + this[7] + ', ' + this[11] + ', ' + this[15] +')'; + }; + + return Matrix4; +}); + +/*global define*/ +define('Core/BoundingSphere',[ + './defaultValue', + './defined', + './DeveloperError', + './Cartesian3', + './Cartesian4', + './Cartographic', + './Ellipsoid', + './GeographicProjection', + './Intersect', + './Interval', + './Matrix4' + ], function( + defaultValue, + defined, + DeveloperError, + Cartesian3, + Cartesian4, + Cartographic, + Ellipsoid, + GeographicProjection, + Intersect, + Interval, + Matrix4) { + "use strict"; + + /** + * A bounding sphere with a center and a radius. + * @alias BoundingSphere + * @constructor + * + * @param {Cartesian3} [center=Cartesian3.ZERO] The center of the bounding sphere. + * @param {Number} [radius=0.0] The radius of the bounding sphere. + * + * @see AxisAlignedBoundingBox + * @see BoundingRectangle + */ + var BoundingSphere = function(center, radius) { + /** + * The center point of the sphere. + * @type {Cartesian3} + * @default {@link Cartesian3.ZERO} + */ + this.center = Cartesian3.clone(defaultValue(center, Cartesian3.ZERO)); + + /** + * The radius of the sphere. + * @type {Number} + * @default 0.0 + */ + this.radius = defaultValue(radius, 0.0); + }; + + var fromPointsXMin = new Cartesian3(); + var fromPointsYMin = new Cartesian3(); + var fromPointsZMin = new Cartesian3(); + var fromPointsXMax = new Cartesian3(); + var fromPointsYMax = new Cartesian3(); + var fromPointsZMax = new Cartesian3(); + var fromPointsCurrentPos = new Cartesian3(); + var fromPointsScratch = new Cartesian3(); + var fromPointsRitterCenter = new Cartesian3(); + var fromPointsMinBoxPt = new Cartesian3(); + var fromPointsMaxBoxPt = new Cartesian3(); + var fromPointsNaiveCenterScratch = new Cartesian3(); + + /** + * Computes a tight-fitting bounding sphere enclosing a list of 3D Cartesian points. + * The bounding sphere is computed by running two algorithms, a naive algorithm and + * Ritter's algorithm. The smaller of the two spheres is used to ensure a tight fit. + * @memberof BoundingSphere + * + * @param {Array} positions An array of points that the bounding sphere will enclose. Each point must have <code>x</code>, <code>y</code>, and <code>z</code> properties. + * @param {BoundingSphere} [result] The object onto which to store the result. + * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if one was not provided. + * + * @see <a href='http://blogs.agi.com/insight3d/index.php/2008/02/04/a-bounding/'>Bounding Sphere computation article</a> + */ + BoundingSphere.fromPoints = function(positions, result) { + if (!defined(result)) { + result = new BoundingSphere(); + } + + if (!defined(positions) || positions.length === 0) { + result.center = Cartesian3.clone(Cartesian3.ZERO, result.center); + result.radius = 0.0; + return result; + } + + var currentPos = Cartesian3.clone(positions[0], fromPointsCurrentPos); + + var xMin = Cartesian3.clone(currentPos, fromPointsXMin); + var yMin = Cartesian3.clone(currentPos, fromPointsYMin); + var zMin = Cartesian3.clone(currentPos, fromPointsZMin); + + var xMax = Cartesian3.clone(currentPos, fromPointsXMax); + var yMax = Cartesian3.clone(currentPos, fromPointsYMax); + var zMax = Cartesian3.clone(currentPos, fromPointsZMax); + + var numPositions = positions.length; + for ( var i = 1; i < numPositions; i++) { + Cartesian3.clone(positions[i], currentPos); + + var x = currentPos.x; + var y = currentPos.y; + var z = currentPos.z; + + // Store points containing the the smallest and largest components + if (x < xMin.x) { + Cartesian3.clone(currentPos, xMin); + } + + if (x > xMax.x) { + Cartesian3.clone(currentPos, xMax); + } + + if (y < yMin.y) { + Cartesian3.clone(currentPos, yMin); + } + + if (y > yMax.y) { + Cartesian3.clone(currentPos, yMax); + } + + if (z < zMin.z) { + Cartesian3.clone(currentPos, zMin); + } + + if (z > zMax.z) { + Cartesian3.clone(currentPos, zMax); + } + } + + // Compute x-, y-, and z-spans (Squared distances b/n each component's min. and max.). + var xSpan = Cartesian3.magnitudeSquared(Cartesian3.subtract(xMax, xMin, fromPointsScratch)); + var ySpan = Cartesian3.magnitudeSquared(Cartesian3.subtract(yMax, yMin, fromPointsScratch)); + var zSpan = Cartesian3.magnitudeSquared(Cartesian3.subtract(zMax, zMin, fromPointsScratch)); + + // Set the diameter endpoints to the largest span. + var diameter1 = xMin; + var diameter2 = xMax; + var maxSpan = xSpan; + if (ySpan > maxSpan) { + maxSpan = ySpan; + diameter1 = yMin; + diameter2 = yMax; + } + if (zSpan > maxSpan) { + maxSpan = zSpan; + diameter1 = zMin; + diameter2 = zMax; + } + + // Calculate the center of the initial sphere found by Ritter's algorithm + var ritterCenter = fromPointsRitterCenter; + ritterCenter.x = (diameter1.x + diameter2.x) * 0.5; + ritterCenter.y = (diameter1.y + diameter2.y) * 0.5; + ritterCenter.z = (diameter1.z + diameter2.z) * 0.5; + + // Calculate the radius of the initial sphere found by Ritter's algorithm + var radiusSquared = Cartesian3.magnitudeSquared(Cartesian3.subtract(diameter2, ritterCenter, fromPointsScratch)); + var ritterRadius = Math.sqrt(radiusSquared); + + // Find the center of the sphere found using the Naive method. + var minBoxPt = fromPointsMinBoxPt; + minBoxPt.x = xMin.x; + minBoxPt.y = yMin.y; + minBoxPt.z = zMin.z; + + var maxBoxPt = fromPointsMaxBoxPt; + maxBoxPt.x = xMax.x; + maxBoxPt.y = yMax.y; + maxBoxPt.z = zMax.z; + + var naiveCenter = Cartesian3.multiplyByScalar(Cartesian3.add(minBoxPt, maxBoxPt, fromPointsScratch), 0.5, fromPointsNaiveCenterScratch); + + // Begin 2nd pass to find naive radius and modify the ritter sphere. + var naiveRadius = 0; + for (i = 0; i < numPositions; i++) { + Cartesian3.clone(positions[i], currentPos); + + // Find the furthest point from the naive center to calculate the naive radius. + var r = Cartesian3.magnitude(Cartesian3.subtract(currentPos, naiveCenter, fromPointsScratch)); + if (r > naiveRadius) { + naiveRadius = r; + } + + // Make adjustments to the Ritter Sphere to include all points. + var oldCenterToPointSquared = Cartesian3.magnitudeSquared(Cartesian3.subtract(currentPos, ritterCenter, fromPointsScratch)); + if (oldCenterToPointSquared > radiusSquared) { + var oldCenterToPoint = Math.sqrt(oldCenterToPointSquared); + // Calculate new radius to include the point that lies outside + ritterRadius = (ritterRadius + oldCenterToPoint) * 0.5; + radiusSquared = ritterRadius * ritterRadius; + // Calculate center of new Ritter sphere + var oldToNew = oldCenterToPoint - ritterRadius; + ritterCenter.x = (ritterRadius * ritterCenter.x + oldToNew * currentPos.x) / oldCenterToPoint; + ritterCenter.y = (ritterRadius * ritterCenter.y + oldToNew * currentPos.y) / oldCenterToPoint; + ritterCenter.z = (ritterRadius * ritterCenter.z + oldToNew * currentPos.z) / oldCenterToPoint; + } + } + + if (ritterRadius < naiveRadius) { + Cartesian3.clone(ritterCenter, result.center); + result.radius = ritterRadius; + } else { + Cartesian3.clone(naiveCenter, result.center); + result.radius = naiveRadius; + } + + return result; + }; + + var defaultProjection = new GeographicProjection(); + var fromExtent2DLowerLeft = new Cartesian3(); + var fromExtent2DUpperRight = new Cartesian3(); + var fromExtent2DSouthwest = new Cartographic(); + var fromExtent2DNortheast = new Cartographic(); + + /** + * Computes a bounding sphere from an extent projected in 2D. + * + * @memberof BoundingSphere + * + * @param {Extent} extent The extent around which to create a bounding sphere. + * @param {Object} [projection=GeographicProjection] The projection used to project the extent into 2D. + * @param {BoundingSphere} [result] The object onto which to store the result. + * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. + */ + BoundingSphere.fromExtent2D = function(extent, projection, result) { + return BoundingSphere.fromExtentWithHeights2D(extent, projection, 0.0, 0.0, result); + }; + + /** + * Computes a bounding sphere from an extent projected in 2D. The bounding sphere accounts for the + * object's minimum and maximum heights over the extent. + * + * @memberof BoundingSphere + * + * @param {Extent} extent The extent around which to create a bounding sphere. + * @param {Object} [projection=GeographicProjection] The projection used to project the extent into 2D. + * @param {Number} [minimumHeight=0.0] The minimum height over the extent. + * @param {Number} [maximumHeight=0.0] The maximum height over the extent. + * @param {BoundingSphere} [result] The object onto which to store the result. + * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. + */ + BoundingSphere.fromExtentWithHeights2D = function(extent, projection, minimumHeight, maximumHeight, result) { + if (!defined(result)) { + result = new BoundingSphere(); + } + + if (!defined(extent)) { + result.center = Cartesian3.clone(Cartesian3.ZERO, result.center); + result.radius = 0.0; + return result; + } + + projection = defaultValue(projection, defaultProjection); + + extent.getSouthwest(fromExtent2DSouthwest); + fromExtent2DSouthwest.height = minimumHeight; + extent.getNortheast(fromExtent2DNortheast); + fromExtent2DNortheast.height = maximumHeight; + + var lowerLeft = projection.project(fromExtent2DSouthwest, fromExtent2DLowerLeft); + var upperRight = projection.project(fromExtent2DNortheast, fromExtent2DUpperRight); + + var width = upperRight.x - lowerLeft.x; + var height = upperRight.y - lowerLeft.y; + var elevation = upperRight.z - lowerLeft.z; + + result.radius = Math.sqrt(width * width + height * height + elevation * elevation) * 0.5; + var center = result.center; + center.x = lowerLeft.x + width * 0.5; + center.y = lowerLeft.y + height * 0.5; + center.z = lowerLeft.z + elevation * 0.5; + return result; + }; + + var fromExtent3DScratch = []; + + /** + * Computes a bounding sphere from an extent in 3D. The bounding sphere is created using a subsample of points + * on the ellipsoid and contained in the extent. It may not be accurate for all extents on all types of ellipsoids. + * @memberof BoundingSphere + * + * @param {Extent} extent The valid extent used to create a bounding sphere. + * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid used to determine positions of the extent. + * @param {Number} [surfaceHeight=0.0] The height above the surface of the ellipsoid. + * @param {BoundingSphere} [result] The object onto which to store the result. + * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. + */ + BoundingSphere.fromExtent3D = function(extent, ellipsoid, surfaceHeight, result) { + ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); + surfaceHeight = defaultValue(surfaceHeight, 0.0); + + var positions; + if (defined(extent)) { + positions = extent.subsample(ellipsoid, surfaceHeight, fromExtent3DScratch); + } + + return BoundingSphere.fromPoints(positions, result); + }; + + /** + * Computes a tight-fitting bounding sphere enclosing a list of 3D points, where the points are + * stored in a flat array in X, Y, Z, order. The bounding sphere is computed by running two + * algorithms, a naive algorithm and Ritter's algorithm. The smaller of the two spheres is used to + * ensure a tight fit. + * + * @memberof BoundingSphere + * + * @param {Array} positions An array of points that the bounding sphere will enclose. Each point + * is formed from three elements in the array in the order X, Y, Z. + * @param {Cartesian3} [center=Cartesian3.ZERO] The position to which the positions are relative, which need not be the + * origin of the coordinate system. This is useful when the positions are to be used for + * relative-to-center (RTC) rendering. + * @param {Number} [stride=3] The number of array elements per vertex. It must be at least 3, but it may + * be higher. Regardless of the value of this parameter, the X coordinate of the first position + * is at array index 0, the Y coordinate is at array index 1, and the Z coordinate is at array index + * 2. When stride is 3, the X coordinate of the next position then begins at array index 3. If + * the stride is 5, however, two array elements are skipped and the next position begins at array + * index 5. + * @param {BoundingSphere} [result] The object onto which to store the result. + * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if one was not provided. + * + * @see <a href='http://blogs.agi.com/insight3d/index.php/2008/02/04/a-bounding/'>Bounding Sphere computation article</a> + * + * @example + * // Compute the bounding sphere from 3 positions, each specified relative to a center. + * // In addition to the X, Y, and Z coordinates, the points array contains two additional + * // elements per point which are ignored for the purpose of computing the bounding sphere. + * var center = new Cartesian3(1.0, 2.0, 3.0); + * var points = [1.0, 2.0, 3.0, 0.1, 0.2, + * 4.0, 5.0, 6.0, 0.1, 0.2, + * 7.0, 8.0, 9.0, 0.1, 0.2]; + * var sphere = BoundingSphere.fromVertices(points, center, 5); + */ + BoundingSphere.fromVertices = function(positions, center, stride, result) { + if (!defined(result)) { + result = new BoundingSphere(); + } + + if (!defined(positions) || positions.length === 0) { + result.center = Cartesian3.clone(Cartesian3.ZERO, result.center); + result.radius = 0.0; + return result; + } + + center = defaultValue(center, Cartesian3.ZERO); + + stride = defaultValue(stride, 3); + + if (stride < 3) { + throw new DeveloperError('stride must be 3 or greater.'); + } + + var currentPos = fromPointsCurrentPos; + currentPos.x = positions[0] + center.x; + currentPos.y = positions[1] + center.y; + currentPos.z = positions[2] + center.z; + + var xMin = Cartesian3.clone(currentPos, fromPointsXMin); + var yMin = Cartesian3.clone(currentPos, fromPointsYMin); + var zMin = Cartesian3.clone(currentPos, fromPointsZMin); + + var xMax = Cartesian3.clone(currentPos, fromPointsXMax); + var yMax = Cartesian3.clone(currentPos, fromPointsYMax); + var zMax = Cartesian3.clone(currentPos, fromPointsZMax); + + var numElements = positions.length; + for (var i = 0; i < numElements; i += stride) { + var x = positions[i] + center.x; + var y = positions[i + 1] + center.y; + var z = positions[i + 2] + center.z; + + currentPos.x = x; + currentPos.y = y; + currentPos.z = z; + + // Store points containing the the smallest and largest components + if (x < xMin.x) { + Cartesian3.clone(currentPos, xMin); + } + + if (x > xMax.x) { + Cartesian3.clone(currentPos, xMax); + } + + if (y < yMin.y) { + Cartesian3.clone(currentPos, yMin); + } + + if (y > yMax.y) { + Cartesian3.clone(currentPos, yMax); + } + + if (z < zMin.z) { + Cartesian3.clone(currentPos, zMin); + } + + if (z > zMax.z) { + Cartesian3.clone(currentPos, zMax); + } + } + + // Compute x-, y-, and z-spans (Squared distances b/n each component's min. and max.). + var xSpan = Cartesian3.magnitudeSquared(Cartesian3.subtract(xMax, xMin, fromPointsScratch)); + var ySpan = Cartesian3.magnitudeSquared(Cartesian3.subtract(yMax, yMin, fromPointsScratch)); + var zSpan = Cartesian3.magnitudeSquared(Cartesian3.subtract(zMax, zMin, fromPointsScratch)); + + // Set the diameter endpoints to the largest span. + var diameter1 = xMin; + var diameter2 = xMax; + var maxSpan = xSpan; + if (ySpan > maxSpan) { + maxSpan = ySpan; + diameter1 = yMin; + diameter2 = yMax; + } + if (zSpan > maxSpan) { + maxSpan = zSpan; + diameter1 = zMin; + diameter2 = zMax; + } + + // Calculate the center of the initial sphere found by Ritter's algorithm + var ritterCenter = fromPointsRitterCenter; + ritterCenter.x = (diameter1.x + diameter2.x) * 0.5; + ritterCenter.y = (diameter1.y + diameter2.y) * 0.5; + ritterCenter.z = (diameter1.z + diameter2.z) * 0.5; + + // Calculate the radius of the initial sphere found by Ritter's algorithm + var radiusSquared = Cartesian3.magnitudeSquared(Cartesian3.subtract(diameter2, ritterCenter, fromPointsScratch)); + var ritterRadius = Math.sqrt(radiusSquared); + + // Find the center of the sphere found using the Naive method. + var minBoxPt = fromPointsMinBoxPt; + minBoxPt.x = xMin.x; + minBoxPt.y = yMin.y; + minBoxPt.z = zMin.z; + + var maxBoxPt = fromPointsMaxBoxPt; + maxBoxPt.x = xMax.x; + maxBoxPt.y = yMax.y; + maxBoxPt.z = zMax.z; + + var naiveCenter = Cartesian3.multiplyByScalar(Cartesian3.add(minBoxPt, maxBoxPt, fromPointsScratch), 0.5, fromPointsNaiveCenterScratch); + + // Begin 2nd pass to find naive radius and modify the ritter sphere. + var naiveRadius = 0; + for (i = 0; i < numElements; i += stride) { + currentPos.x = positions[i] + center.x; + currentPos.y = positions[i + 1] + center.y; + currentPos.z = positions[i + 2] + center.z; + + // Find the furthest point from the naive center to calculate the naive radius. + var r = Cartesian3.magnitude(Cartesian3.subtract(currentPos, naiveCenter, fromPointsScratch)); + if (r > naiveRadius) { + naiveRadius = r; + } + + // Make adjustments to the Ritter Sphere to include all points. + var oldCenterToPointSquared = Cartesian3.magnitudeSquared(Cartesian3.subtract(currentPos, ritterCenter, fromPointsScratch)); + if (oldCenterToPointSquared > radiusSquared) { + var oldCenterToPoint = Math.sqrt(oldCenterToPointSquared); + // Calculate new radius to include the point that lies outside + ritterRadius = (ritterRadius + oldCenterToPoint) * 0.5; + radiusSquared = ritterRadius * ritterRadius; + // Calculate center of new Ritter sphere + var oldToNew = oldCenterToPoint - ritterRadius; + ritterCenter.x = (ritterRadius * ritterCenter.x + oldToNew * currentPos.x) / oldCenterToPoint; + ritterCenter.y = (ritterRadius * ritterCenter.y + oldToNew * currentPos.y) / oldCenterToPoint; + ritterCenter.z = (ritterRadius * ritterCenter.z + oldToNew * currentPos.z) / oldCenterToPoint; + } + } + + if (ritterRadius < naiveRadius) { + Cartesian3.clone(ritterCenter, result.center); + result.radius = ritterRadius; + } else { + Cartesian3.clone(naiveCenter, result.center); + result.radius = naiveRadius; + } + + return result; + }; + + /** + * Computes a bounding sphere from the corner points of an axis-aligned bounding box. The sphere + * tighly and fully encompases the box. + * + * @memberof BoundingSphere + * + * @param {Number} [corner] The minimum height over the extent. + * @param {Number} [oppositeCorner] The maximum height over the extent. + * @param {BoundingSphere} [result] The object onto which to store the result. + * + * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. + * + * @exception {DeveloperError} corner and oppositeCorner are required. + * + * @example + * // Create a bounding sphere around the unit cube + * var sphere = BoundingSphere.fromCornerPoints(new Cartesian3(-0.5, -0.5, -0.5), new Cartesian3(0.5, 0.5, 0.5)); + */ + BoundingSphere.fromCornerPoints = function(corner, oppositeCorner, result) { + if (!defined(corner) || !defined(oppositeCorner)) { + throw new DeveloperError('corner and oppositeCorner are required.'); + } + + if (!defined(result)) { + result = new BoundingSphere(); + } + + var center = result.center; + Cartesian3.add(corner, oppositeCorner, center); + Cartesian3.multiplyByScalar(center, 0.5, center); + result.radius = Cartesian3.distance(center, oppositeCorner); + return result; + }; + + /** + * Creates a bounding sphere encompassing an ellipsoid. + * + * @memberof BoundingSphere + * + * @param {Ellipsoid} ellipsoid The ellipsoid around which to create a bounding sphere. + * @param {BoundingSphere} [result] The object onto which to store the result. + * + * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. + * + * @exception {DeveloperError} ellipsoid is required. + * + * @example + * var boundingSphere = BoundingSphere.fromEllipsoid(ellipsoid); + */ + BoundingSphere.fromEllipsoid = function(ellipsoid, result) { + if (!defined(ellipsoid)) { + throw new DeveloperError('ellipsoid is required.'); + } + + if (!defined(result)) { + result = new BoundingSphere(); + } + + Cartesian3.clone(Cartesian3.ZERO, result.center); + result.radius = ellipsoid.getMaximumRadius(); + return result; + }; + + /** + * Duplicates a BoundingSphere instance. + * @memberof BoundingSphere + * + * @param {BoundingSphere} sphere The bounding sphere to duplicate. + * @param {BoundingSphere} [result] The object onto which to store the result. + * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. (Returns undefined if sphere is undefined) + */ + BoundingSphere.clone = function(sphere, result) { + if (!defined(sphere)) { + return undefined; + } + + if (!defined(result)) { + return new BoundingSphere(sphere.center, sphere.radius); + } + + result.center = Cartesian3.clone(sphere.center, result.center); + result.radius = sphere.radius; + return result; + }; + + var unionScratch = new Cartesian3(); + var unionScratchCenter = new Cartesian3(); + /** + * Computes a bounding sphere that contains both the left and right bounding spheres. + * @memberof BoundingSphere + * + * @param {BoundingSphere} left A sphere to enclose in a bounding sphere. + * @param {BoundingSphere} right A sphere to enclose in a bounding sphere. + * @param {BoundingSphere} [result] The object onto which to store the result. + * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + */ + BoundingSphere.union = function(left, right, result) { + if (!defined(left)) { + throw new DeveloperError('left is required.'); + } + + if (!defined(right)) { + throw new DeveloperError('right is required.'); + } + + if (!defined(result)) { + result = new BoundingSphere(); + } + + var leftCenter = left.center; + var rightCenter = right.center; + + Cartesian3.add(leftCenter, rightCenter, unionScratchCenter); + var center = Cartesian3.multiplyByScalar(unionScratchCenter, 0.5, unionScratchCenter); + + var radius1 = Cartesian3.magnitude(Cartesian3.subtract(leftCenter, center, unionScratch)) + left.radius; + var radius2 = Cartesian3.magnitude(Cartesian3.subtract(rightCenter, center, unionScratch)) + right.radius; + + result.radius = Math.max(radius1, radius2); + Cartesian3.clone(center, result.center); + + return result; + }; + + var expandScratch = new Cartesian3(); + /** + * Computes a bounding sphere by enlarging the provided sphere to contain the provided point. + * @memberof BoundingSphere + * + * @param {BoundingSphere} sphere A sphere to expand. + * @param {Cartesian3} point A point to enclose in a bounding sphere. + * @param {BoundingSphere} [result] The object onto which to store the result. + * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. + * + * @exception {DeveloperError} sphere is required. + * @exception {DeveloperError} point is required. + */ + BoundingSphere.expand = function(sphere, point, result) { + if (!defined(sphere)) { + throw new DeveloperError('sphere is required.'); + } + + if (!defined(point)) { + throw new DeveloperError('point is required.'); + } + + result = BoundingSphere.clone(sphere, result); + + var radius = Cartesian3.magnitude(Cartesian3.subtract(point, result.center, expandScratch)); + if (radius > result.radius) { + result.radius = radius; + } + + return result; + }; + + /** + * Determines which side of a plane a sphere is located. + * @memberof BoundingSphere + * + * @param {BoundingSphere} sphere The bounding sphere to test. + * @param {Cartesian4} plane The coefficients of the plane in the for ax + by + cz + d = 0 + * where the coefficients a, b, c, and d are the components x, y, z, + * and w of the {Cartesian4}, respectively. + * @returns {Intersect} {Intersect.INSIDE} if the entire sphere is on the side of the plane the normal + * is pointing, {Intersect.OUTSIDE} if the entire sphere is on the opposite side, + * and {Intersect.INTERSETING} if the sphere intersects the plane. + * + * @exception {DeveloperError} sphere is required. + * @exception {DeveloperError} plane is required. + */ + BoundingSphere.intersect = function(sphere, plane) { + if (!defined(sphere)) { + throw new DeveloperError('sphere is required.'); + } + + if (!defined(plane)) { + throw new DeveloperError('plane is required.'); + } + + var center = sphere.center; + var radius = sphere.radius; + var distanceToPlane = Cartesian3.dot(plane, center) + plane.w; + + if (distanceToPlane < -radius) { + // The center point is negative side of the plane normal + return Intersect.OUTSIDE; + } else if (distanceToPlane < radius) { + // The center point is positive side of the plane, but radius extends beyond it; partial overlap + return Intersect.INTERSECTING; + } + return Intersect.INSIDE; + }; + + var columnScratch = new Cartesian3(); + + /** + * Applies a 4x4 affine transformation matrix to a bounding sphere. + * @memberof BoundingSphere + * + * @param {BoundingSphere} sphere The bounding sphere to apply the transformation to. + * @param {Matrix4} transform The transformation matrix to apply to the bounding sphere. + * @param {BoundingSphere} [result] The object onto which to store the result. + * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. + * + * @exception {DeveloperError} sphere is required. + * @exception {DeveloperError} transform is required. + */ + BoundingSphere.transform = function(sphere, transform, result) { + if (!defined(sphere)) { + throw new DeveloperError('sphere is required.'); + } + + if (!defined(transform)) { + throw new DeveloperError('transform is required.'); + } + + if (!defined(result)) { + result = new BoundingSphere(); + } + + result.center = Matrix4.multiplyByPoint(transform, sphere.center, result.center); + result.radius = Math.max(Cartesian3.magnitude(Matrix4.getColumn(transform, 0, columnScratch)), + Cartesian3.magnitude(Matrix4.getColumn(transform, 1, columnScratch)), + Cartesian3.magnitude(Matrix4.getColumn(transform, 2, columnScratch))) * sphere.radius; + + return result; + }; + + /** + * Applies a 4x4 affine transformation matrix to a bounding sphere where there is no scale + * The transformation matrix is not verified to have a uniform scale of 1. + * This method is faster than computing the general bounding sphere transform using {@link #transform}. + * @memberof BoundingSphere + * + * @param {BoundingSphere} sphere The bounding sphere to apply the transformation to. + * @param {Matrix4} transform The transformation matrix to apply to the bounding sphere. + * @param {BoundingSphere} [result] The object onto which to store the result. + * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. + * + * @exception {DeveloperError} sphere is required. + * @exception {DeveloperError} transform is required. + * + * @example + * var modelMatrix = Transforms.eastNorthUpToFixedFrame(positionOnEllipsoid); + * var boundingSphere = new BoundingSphere(); + * var newBoundingSphere = BoundingSphere.transformWithoutScale(boundingSphere, modelMatrix); + */ + BoundingSphere.transformWithoutScale = function(sphere, transform, result) { + if (!defined(sphere)) { + throw new DeveloperError('sphere is required.'); + } + + if (!defined(transform)) { + throw new DeveloperError('transform is required.'); + } + + if (!defined(result)) { + result = new BoundingSphere(); + } + + result.center = Matrix4.multiplyByPoint(transform, sphere.center, result.center); + result.radius = sphere.radius; + + return result; + }; + + var scratchCartesian3 = new Cartesian3(); + /** + * The distances calculated by the vector from the center of the bounding sphere to position projected onto direction + * plus/minus the radius of the bounding sphere. + * <br> + * If you imagine the infinite number of planes with normal direction, this computes the smallest distance to the + * closest and farthest planes from position that intersect the bounding sphere. + * @memberof BoundingSphere + * + * @param {BoundingSphere} sphere The bounding sphere to calculate the distance to. + * @param {Cartesian3} position The position to calculate the distance from. + * @param {Cartesian3} direction The direction from position. + * @param {Cartesian2} [result] A Cartesian2 to store the nearest and farthest distances. + * @returns {Interval} The nearest and farthest distances on the bounding sphere from position in direction. + * + * @exception {DeveloperError} sphere is required. + * @exception {DeveloperError} position is required. + * @exception {DeveloperError} direction is required. + */ + BoundingSphere.getPlaneDistances = function(sphere, position, direction, result) { + if (!defined(sphere)) { + throw new DeveloperError('sphere is required.'); + } + + if (!defined(position)) { + throw new DeveloperError('position is required.'); + } + + if (!defined(direction)) { + throw new DeveloperError('direction is required.'); + } + + if (!defined(result)) { + result = new Interval(); + } + + var toCenter = Cartesian3.subtract(sphere.center, position, scratchCartesian3); + var proj = Cartesian3.multiplyByScalar(direction, Cartesian3.dot(direction, toCenter), scratchCartesian3); + var mag = Cartesian3.magnitude(proj); + + result.start = mag - sphere.radius; + result.stop = mag + sphere.radius; + return result; + }; + + var projectTo2DNormalScratch = new Cartesian3(); + var projectTo2DEastScratch = new Cartesian3(); + var projectTo2DNorthScratch = new Cartesian3(); + var projectTo2DWestScratch = new Cartesian3(); + var projectTo2DSouthScratch = new Cartesian3(); + var projectTo2DCartographicScratch = new Cartographic(); + var projectTo2DPositionsScratch = new Array(8); + for (var n = 0; n < 8; ++n) { + projectTo2DPositionsScratch[n] = new Cartesian3(); + } + var projectTo2DProjection = new GeographicProjection(); + /** + * Creates a bounding sphere in 2D from a bounding sphere in 3D world coordinates. + * @memberof BoundingSphere + * + * @param {BoundingSphere} sphere The bounding sphere to transform to 2D. + * @param {Object} [projection=GeographicProjection] The projection to 2D. + * @param {BoundingSphere} [result] The object onto which to store the result. + * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. + * + * @exception {DeveloperError} sphere is required. + */ + BoundingSphere.projectTo2D = function(sphere, projection, result) { + if (!defined(sphere)) { + throw new DeveloperError('sphere is required.'); + } + + projection = defaultValue(projection, projectTo2DProjection); + + var ellipsoid = projection.getEllipsoid(); + var center = sphere.center; + var radius = sphere.radius; + + var normal = ellipsoid.geodeticSurfaceNormal(center, projectTo2DNormalScratch); + var east = Cartesian3.cross(Cartesian3.UNIT_Z, normal, projectTo2DEastScratch); + Cartesian3.normalize(east, east); + var north = Cartesian3.cross(normal, east, projectTo2DNorthScratch); + Cartesian3.normalize(north, north); + + Cartesian3.multiplyByScalar(normal, radius, normal); + Cartesian3.multiplyByScalar(north, radius, north); + Cartesian3.multiplyByScalar(east, radius, east); + + var south = Cartesian3.negate(north, projectTo2DSouthScratch); + var west = Cartesian3.negate(east, projectTo2DWestScratch); + + var positions = projectTo2DPositionsScratch; + + // top NE corner + var corner = positions[0]; + Cartesian3.add(normal, north, corner); + Cartesian3.add(corner, east, corner); + + // top NW corner + corner = positions[1]; + Cartesian3.add(normal, north, corner); + Cartesian3.add(corner, west, corner); + + // top SW corner + corner = positions[2]; + Cartesian3.add(normal, south, corner); + Cartesian3.add(corner, west, corner); + + // top SE corner + corner = positions[3]; + Cartesian3.add(normal, south, corner); + Cartesian3.add(corner, east, corner); + + Cartesian3.negate(normal, normal); + + // bottom NE corner + corner = positions[4]; + Cartesian3.add(normal, north, corner); + Cartesian3.add(corner, east, corner); + + // bottom NW corner + corner = positions[5]; + Cartesian3.add(normal, north, corner); + Cartesian3.add(corner, west, corner); + + // bottom SW corner + corner = positions[6]; + Cartesian3.add(normal, south, corner); + Cartesian3.add(corner, west, corner); + + // bottom SE corner + corner = positions[7]; + Cartesian3.add(normal, south, corner); + Cartesian3.add(corner, east, corner); + + var length = positions.length; + for (var i = 0; i < length; ++i) { + var position = positions[i]; + Cartesian3.add(center, position, position); + var cartographic = ellipsoid.cartesianToCartographic(position, projectTo2DCartographicScratch); + projection.project(cartographic, position); + } + + result = BoundingSphere.fromPoints(positions, result); + + // swizzle center components + center = result.center; + var x = center.x; + var y = center.y; + var z = center.z; + center.x = z; + center.y = x; + center.z = y; + + return result; + }; + + /** + * Compares the provided BoundingSphere componentwise and returns + * <code>true</code> if they are equal, <code>false</code> otherwise. + * @memberof BoundingSphere + * + * @param {BoundingSphere} [left] The first BoundingSphere. + * @param {BoundingSphere} [right] The second BoundingSphere. + * @returns {Boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise. + */ + BoundingSphere.equals = function(left, right) { + return (left === right) || + ((defined(left)) && + (defined(right)) && + Cartesian3.equals(left.center, right.center) && + left.radius === right.radius); + }; + + /** + * Duplicates this BoundingSphere instance. + * @memberof BoundingSphere + * + * @param {BoundingSphere} [result] The object onto which to store the result. + * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. + */ + BoundingSphere.prototype.clone = function(result) { + return BoundingSphere.clone(this, result); + }; + + /** + * Computes a bounding sphere that contains both this bounding sphere and the argument sphere. + * @memberof BoundingSphere + * + * @param {BoundingSphere} right The sphere to enclose in this bounding sphere. + * @param {BoundingSphere} [result] The object onto which to store the result. + * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. + * + * @exception {DeveloperError} sphere is required. + */ + BoundingSphere.prototype.union = function(right, result) { + return BoundingSphere.union(this, right, result); + }; + + /** + * Computes a bounding sphere that is sphere expanded to contain point. + * @memberof BoundingSphere + * + * @param {Cartesian3} point A point to enclose in a bounding sphere. + * @param {BoundingSphere} [result] The object onto which to store the result. + * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if one was not provided. + * + * @exception {DeveloperError} point is required. + */ + BoundingSphere.prototype.expand = function(point, result) { + return BoundingSphere.expand(this, point, result); + }; + + /** + * Determines which side of a plane the sphere is located. + * @memberof BoundingSphere + * + * @param {Cartesian4} plane The coefficients of the plane in the for ax + by + cz + d = 0 + * where the coefficients a, b, c, and d are the components x, y, z, + * and w of the {Cartesian4}, respectively. + * @returns {Intersect} {Intersect.INSIDE} if the entire sphere is on the side of the plane the normal + * is pointing, {Intersect.OUTSIDE} if the entire sphere is on the opposite side, + * and {Intersect.INTERSETING} if the sphere intersects the plane. + * + * @exception {DeveloperError} plane is required. + */ + BoundingSphere.prototype.intersect = function(plane) { + return BoundingSphere.intersect(this, plane); + }; + + /** + * The distances calculated by the vector from the center of the bounding sphere to position projected onto direction + * plus/minus the radius of the bounding sphere. + * <br> + * If you imagine the infinite number of planes with normal direction, this computes the smallest distance to the + * closest and farthest planes from position that intersect the bounding sphere. + * @memberof BoundingSphere + * + * @param {Cartesian3} position The position to calculate the distance from. + * @param {Cartesian3} direction The direction from position. + * @param {Cartesian2} [result] A Cartesian2 to store the nearest and farthest distances. + * @returns {Interval} The nearest and farthest distances on the bounding sphere from position in direction. + * + * @exception {DeveloperError} position is required. + * @exception {DeveloperError} direction is required. + */ + BoundingSphere.prototype.getPlaneDistances = function(position, direction, result) { + return BoundingSphere.getPlaneDistances(this, position, direction, result); + }; + + /** + * Creates a bounding sphere in 2D from this bounding sphere. This bounding sphere must be in 3D world coordinates. + * @memberof BoundingSphere + * + * @param {Object} [projection=GeographicProjection] The projection to 2D. + * @param {BoundingSphere} [result] The object onto which to store the result. + * @returns {BoundingSphere} The modified result parameter or a new BoundingSphere instance if none was provided. + */ + BoundingSphere.prototype.projectTo2D = function(projection, result) { + return BoundingSphere.projectTo2D(this, projection, result); + }; + + /** + * Compares this BoundingSphere against the provided BoundingSphere componentwise and returns + * <code>true</code> if they are equal, <code>false</code> otherwise. + * @memberof BoundingSphere + * + * @param {BoundingSphere} [right] The right hand side BoundingSphere. + * @returns {Boolean} <code>true</code> if they are equal, <code>false</code> otherwise. + */ + BoundingSphere.prototype.equals = function(right) { + return BoundingSphere.equals(this, right); + }; + + return BoundingSphere; +}); + +/*global define*/ +define('Core/Fullscreen',['./defined'], function(defined) { + "use strict"; + + var _supportsFullscreen; + var _names = { + requestFullscreen : undefined, + exitFullscreen : undefined, + fullscreenEnabled : undefined, + fullscreenElement : undefined, + fullscreenchange : undefined, + fullscreenerror : undefined + }; + + /** + * Browser-independent functions for working with the standard fullscreen API. + * + * @exports Fullscreen + * + * @see <a href='http://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html'>W3C Fullscreen Living Specification</a> + */ + var Fullscreen = {}; + + /** + * Detects whether the browser supports the standard fullscreen API. + * + * @returns <code>true</code> if the browser supports the standard fullscreen API, + * <code>false</code> otherwise. + */ + Fullscreen.supportsFullscreen = function() { + if (defined(_supportsFullscreen)) { + return _supportsFullscreen; + } + + _supportsFullscreen = false; + + var body = document.body; + if (typeof body.requestFullscreen === 'function') { + // go with the unprefixed, standard set of names + _names.requestFullscreen = 'requestFullscreen'; + _names.exitFullscreen = 'exitFullscreen'; + _names.fullscreenEnabled = 'fullscreenEnabled'; + _names.fullscreenElement = 'fullscreenElement'; + _names.fullscreenchange = 'fullscreenchange'; + _names.fullscreenerror = 'fullscreenerror'; + _supportsFullscreen = true; + return _supportsFullscreen; + } + + //check for the correct combination of prefix plus the various names that browsers use + var prefixes = ['webkit', 'moz', 'o', 'ms', 'khtml']; + var name; + for ( var i = 0, len = prefixes.length; i < len; ++i) { + var prefix = prefixes[i]; + + // casing of Fullscreen differs across browsers + name = prefix + 'RequestFullscreen'; + if (typeof body[name] === 'function') { + _names.requestFullscreen = name; + _supportsFullscreen = true; + } else { + name = prefix + 'RequestFullScreen'; + if (typeof body[name] === 'function') { + _names.requestFullscreen = name; + _supportsFullscreen = true; + } + } + + // disagreement about whether it's "exit" as per spec, or "cancel" + name = prefix + 'ExitFullscreen'; + if (typeof document[name] === 'function') { + _names.exitFullscreen = name; + } else { + name = prefix + 'CancelFullScreen'; + if (typeof document[name] === 'function') { + _names.exitFullscreen = name; + } + } + + // casing of Fullscreen differs across browsers + name = prefix + 'FullscreenEnabled'; + if (defined(document[name])) { + _names.fullscreenEnabled = name; + } else { + name = prefix + 'FullScreenEnabled'; + if (defined(document[name])) { + _names.fullscreenEnabled = name; + } + } + + // casing of Fullscreen differs across browsers + name = prefix + 'FullscreenElement'; + if (defined(document[name])) { + _names.fullscreenElement = name; + } else { + name = prefix + 'FullScreenElement'; + if (defined(document[name])) { + _names.fullscreenElement = name; + } + } + + // thankfully, event names are all lowercase per spec + name = prefix + 'fullscreenchange'; + // event names do not have 'on' in the front, but the property on the document does + if (defined(document['on' + name])) { + //except on IE + if (prefix === 'ms') { + name = 'MSFullscreenChange'; + } + _names.fullscreenchange = name; + } + + name = prefix + 'fullscreenerror'; + if (defined(document['on' + name])) { + //except on IE + if (prefix === 'ms') { + name = 'MSFullscreenError'; + } + _names.fullscreenerror = name; + } + } + + return _supportsFullscreen; + }; + + /** + * Asynchronously requests the browser to enter fullscreen mode on the given element. + * If fullscreen mode is not supported by the browser, does nothing. + * + * @param {Object} element The HTML element which will be placed into fullscreen mode. + * + * @example + * // Put the entire page into fullscreen. + * Fullscreen.requestFullscreen(document.body) + * + * // Place only the Cesium canvas into fullscreen. + * Fullscreen.requestFullscreen(scene.getCanvas()) + */ + Fullscreen.requestFullscreen = function(element) { + if (!Fullscreen.supportsFullscreen()) { + return; + } + + element[_names.requestFullscreen](); + }; + + /** + * Asynchronously exits fullscreen mode. If the browser is not currently + * in fullscreen, or if fullscreen mode is not supported by the browser, does nothing. + */ + Fullscreen.exitFullscreen = function() { + if (!Fullscreen.supportsFullscreen()) { + return; + } + + document[_names.exitFullscreen](); + }; + + /** + * Determine whether the browser will allow an element to be made fullscreen, or not. + * For example, by default, iframes cannot go fullscreen unless the containing page + * adds an "allowfullscreen" attribute (or prefixed equivalent). + * + * @returns {Boolean} <code>true</code> if the browser is able to enter fullscreen mode, + * <code>false</code> if not, and <code>undefined</code> if the browser does not + * support fullscreen mode. + */ + Fullscreen.isFullscreenEnabled = function() { + if (!Fullscreen.supportsFullscreen()) { + return undefined; + } + + return document[_names.fullscreenEnabled]; + }; + + /** + * Gets the element that is currently fullscreen, if any. To simply check if the + * browser is in fullscreen mode or not, use {@link Fullscreen#isFullscreen}. + * + * @returns {Object} the element that is currently fullscreen, or <code>null</code> if the browser is + * not in fullscreen mode, or <code>undefined</code> if the browser does not support fullscreen + * mode. + */ + Fullscreen.getFullscreenElement = function() { + if (!Fullscreen.supportsFullscreen()) { + return undefined; + } + + return document[_names.fullscreenElement]; + }; + + /** + * Determines if the browser is currently in fullscreen mode. + * + * @returns {Boolean} <code>true</code> if the browser is currently in fullscreen mode, <code>false</code> + * if it is not, or <code>undefined</code> if the browser does not support fullscreen mode. + */ + Fullscreen.isFullscreen = function() { + if (!Fullscreen.supportsFullscreen()) { + return undefined; + } + + return Fullscreen.getFullscreenElement() !== null; + }; + + /** + * Gets the name of the event on the document that is fired when fullscreen is + * entered or exited. This event name is intended for use with addEventListener. + * + * In your event handler, to determine if the browser is in fullscreen mode or not, + * use {@link Fullscreen#isFullscreen}. + * + * @returns {String} the name of the event that is fired when fullscreen is entered or + * exited, or <code>undefined</code> if fullscreen is not supported. + */ + Fullscreen.getFullscreenChangeEventName = function() { + if (!Fullscreen.supportsFullscreen()) { + return undefined; + } + + return _names.fullscreenchange; + }; + + /** + * Gets the name of the event that is fired when a fullscreen error + * occurs. This event name is intended for use with addEventListener. + * + * @returns {String} the name of the event that is fired when a fullscreen error occurs, + * or <code>undefined</code> if fullscreen is not supported. + */ + Fullscreen.getFullscreenErrorEventName = function() { + if (!Fullscreen.supportsFullscreen()) { + return undefined; + } + + return _names.fullscreenerror; + }; + + return Fullscreen; +}); +/*global define*/ +define('Core/FeatureDetection',[ + './defined', + './Fullscreen' + ], function( + defined, + Fullscreen) { + "use strict"; + + function extractVersion(versionString) { + var parts = versionString.split('.'); + for ( var i = 0, len = parts.length; i < len; ++i) { + parts[i] = parseInt(parts[i], 10); + } + return parts; + } + + var isChromeResult; + var chromeVersionResult; + function isChrome() { + if (!defined(isChromeResult)) { + var fields = (/ Chrome\/([\.0-9]+)/).exec(navigator.userAgent); + if (fields === null) { + isChromeResult = false; + } else { + isChromeResult = true; + chromeVersionResult = extractVersion(fields[1]); + } + } + + return isChromeResult; + } + + function chromeVersion() { + return isChrome() && chromeVersionResult; + } + + var isSafariResult; + var safariVersionResult; + function isSafari() { + if (!defined(isSafariResult)) { + // Chrome contains Safari in the user agent too + if (isChrome() || !(/ Safari\/[\.0-9]+/).test(navigator.userAgent)) { + isSafariResult = false; + } else { + var fields = (/ Version\/([\.0-9]+)/).exec(navigator.userAgent); + if (fields === null) { + isSafariResult = false; + } else { + isSafariResult = true; + safariVersionResult = extractVersion(fields[1]); + } + } + } + + return isSafariResult; + } + + function safariVersion() { + return isSafari() && safariVersionResult; + } + + var isWebkitResult; + var webkitVersionResult; + function isWebkit() { + if (!defined(isWebkitResult)) { + var fields = (/ AppleWebKit\/([\.0-9]+)(\+?)/).exec(navigator.userAgent); + if (fields === null) { + isWebkitResult = false; + } else { + isWebkitResult = true; + webkitVersionResult = extractVersion(fields[1]); + webkitVersionResult.isNightly = !!fields[2]; + } + } + + return isWebkitResult; + } + + function webkitVersion() { + return isWebkit() && webkitVersionResult; + } + + var isInternetExplorerResult; + var internetExplorerVersionResult; + function isInternetExplorer() { + if (!defined(isInternetExplorerResult)) { + var fields = (/ MSIE ([\.0-9]+)/).exec(navigator.userAgent); + if (fields === null) { + isInternetExplorerResult = false; + } else { + isInternetExplorerResult = true; + internetExplorerVersionResult = extractVersion(fields[1]); + } + } + return isInternetExplorerResult; + } + + function internetExplorerVersion() { + return isInternetExplorer() && internetExplorerVersionResult; + } + + /** + * A set of functions to detect whether the current browser supports + * various features. + * + * @exports FeatureDetection + */ + var FeatureDetection = { + isChrome : isChrome, + chromeVersion : chromeVersion, + isSafari : isSafari, + safariVersion : safariVersion, + isWebkit : isWebkit, + webkitVersion : webkitVersion, + isInternetExplorer : isInternetExplorer, + internetExplorerVersion : internetExplorerVersion + }; + + var supportsCrossOriginImagery; + + /** + * Detects whether the current browser supports the use of cross-origin + * requests to load streaming imagery. + * + * @returns true if the browser can load cross-origin streaming imagery, false if not. + * + * @see <a href='http://www.w3.org/TR/cors/'>Cross-Origin Resource Sharing</a> + */ + FeatureDetection.supportsCrossOriginImagery = function() { + if (!defined(supportsCrossOriginImagery)) { + if (isSafari() && webkitVersion()[0] < 536) { + // versions of Safari below this incorrectly throw a DOM error when calling + // readPixels on a canvas containing a cross-origin image. + supportsCrossOriginImagery = false; + } else { + // any other versions of browsers that incorrectly block + // readPixels on canvas containing crossOrigin images? + supportsCrossOriginImagery = 'withCredentials' in new XMLHttpRequest(); + } + } + return supportsCrossOriginImagery; + }; + + /** + * Detects whether the current browser supports the full screen standard. + * + * @returns true if the browser supports the full screen standard, false if not. + * + * @see Fullscreen + * @see <a href='http://dvcs.w3.org/hg/fullscreen/raw-file/tip/Overview.html'>W3C Fullscreen Living Specification</a> + */ + FeatureDetection.supportsFullscreen = function() { + return Fullscreen.supportsFullscreen(); + }; + + /** + * Detects whether the current browser supports typed arrays. + * + * @returns true if the browser supports typed arrays, false if not. + * + * @see <a href='http://www.khronos.org/registry/typedarray/specs/latest/'>Typed Array Specification</a> + */ + FeatureDetection.supportsTypedArrays = function() { + return typeof ArrayBuffer !== 'undefined'; + }; + + return FeatureDetection; +}); +/*global define*/ +define('Core/ComponentDatatype',[ + './defaultValue', + './defined', + './DeveloperError', + './FeatureDetection', + './Enumeration' + ], function( + defaultValue, + defined, + DeveloperError, + FeatureDetection, + Enumeration) { + "use strict"; + + // Bail out if the browser doesn't support typed arrays, to prevent the setup function + // from failing, since we won't be able to create a WebGL context anyway. + if (!FeatureDetection.supportsTypedArrays()) { + return {}; + } + + /** + * Enumerations for WebGL component datatypes. Components are intrinsics, + * which form attributes, which form vertices. + * + * @alias ComponentDatatype + * @enumeration + */ + var ComponentDatatype = { + /** + * 8-bit signed byte enumeration corresponding to <code>gl.BYTE</code> and the type + * of an element in <code>Int8Array</code>. + * + * @type {Enumeration} + * @constant + * @default 0x1400 + */ + BYTE : new Enumeration(0x1400, 'BYTE', { + sizeInBytes : Int8Array.BYTES_PER_ELEMENT + }), + + /** + * 8-bit unsigned byte enumeration corresponding to <code>UNSIGNED_BYTE</code> and the type + * of an element in <code>Uint8Array</code>. + * + * @type {Enumeration} + * @constant + * @default 0x1401 + */ + UNSIGNED_BYTE : new Enumeration(0x1401, 'UNSIGNED_BYTE', { + sizeInBytes : Uint8Array.BYTES_PER_ELEMENT + }), + + /** + * 16-bit signed short enumeration corresponding to <code>SHORT</code> and the type + * of an element in <code>Int16Array</code>. + * + * @type {Enumeration} + * @constant + * @default 0x1402 + */ + SHORT : new Enumeration(0x1402, 'SHORT', { + sizeInBytes : Int16Array.BYTES_PER_ELEMENT + }), + + /** + * 16-bit unsigned short enumeration corresponding to <code>UNSIGNED_SHORT</code> and the type + * of an element in <code>Uint16Array</code>. + * + * @type {Enumeration} + * @constant + * @default 0x1403 + */ + UNSIGNED_SHORT : new Enumeration(0x1403, 'UNSIGNED_SHORT', { + sizeInBytes : Uint16Array.BYTES_PER_ELEMENT + }), + + /** + * 32-bit floating-point enumeration corresponding to <code>FLOAT</code> and the type + * of an element in <code>Float32Array</code>. + * + * @type {Enumeration} + * @constant + * @default 0x1406 + */ + FLOAT : new Enumeration(0x1406, 'FLOAT', { + sizeInBytes : Float32Array.BYTES_PER_ELEMENT + }), + + /** + * 64-bit floating-point enumeration corresponding to <code>gl.DOUBLE</code> (in Desktop OpenGL; + * this is not supported in WebGL, and is emulated in Cesium via {@link GeometryPipeline.encodeAttribute}) + * and the type of an element in <code>Float64Array</code>. + * + * @memberOf ComponentDatatype + * + * @type {Enumeration} + * @constant + * @default 0x140A + */ + DOUBLE : new Enumeration(0x140A, 'DOUBLE', { + sizeInBytes : Float64Array.BYTES_PER_ELEMENT + }) + }; + + /** + * Validates that the provided component datatype is a valid {@link ComponentDatatype} + * + * @param {ComponentDatatype} componentDatatype The component datatype to validate. + * + * @returns {Boolean} <code>true</code> if the provided component datatype is a valid enumeration value; otherwise, <code>false</code>. + * + * @example + * if (!ComponentDatatype.validate(componentDatatype)) { + * throw new DeveloperError('componentDatatype must be a valid enumeration value.'); + * } + */ + ComponentDatatype.validate = function(componentDatatype) { + return defined(componentDatatype) && defined(componentDatatype.value) && + (componentDatatype.value === ComponentDatatype.BYTE.value || + componentDatatype.value === ComponentDatatype.UNSIGNED_BYTE.value || + componentDatatype.value === ComponentDatatype.SHORT.value || + componentDatatype.value === ComponentDatatype.UNSIGNED_SHORT.value || + componentDatatype.value === ComponentDatatype.FLOAT.value || + componentDatatype.value === ComponentDatatype.DOUBLE.value); + }; + + /** + * Creates a typed array corresponding to component data type. + * @memberof ComponentDatatype + * + * @param {ComponentDatatype} componentDatatype The component data type. + * @param {Number|Array} valuesOrLength The length of the array to create or an array. + * + * @returns {Int8Array|Uint8Array|Int16Array|Uint16Array|Float32Array|Float64Array} A typed array. + * + * @exception {DeveloperError} componentDatatype is required. + * @exception {DeveloperError} valuesOrLength is required. + * @exception {DeveloperError} componentDatatype is not a valid enumeration value. + * + * @example + * // creates a Float32Array with length of 100 + * var typedArray = ComponentDatatype.createTypedArray(ComponentDatatype.FLOAT, 100); + */ + ComponentDatatype.createTypedArray = function(componentDatatype, valuesOrLength) { + if (!defined(componentDatatype)) { + throw new DeveloperError('componentDatatype is required.'); + } + + if (!defined(valuesOrLength)) { + throw new DeveloperError('valuesOrLength is required.'); + } + + switch (componentDatatype.value) { + case ComponentDatatype.BYTE.value: + return new Int8Array(valuesOrLength); + case ComponentDatatype.UNSIGNED_BYTE.value: + return new Uint8Array(valuesOrLength); + case ComponentDatatype.SHORT.value: + return new Int16Array(valuesOrLength); + case ComponentDatatype.UNSIGNED_SHORT.value: + return new Uint16Array(valuesOrLength); + case ComponentDatatype.FLOAT.value: + return new Float32Array(valuesOrLength); + case ComponentDatatype.DOUBLE.value: + return new Float64Array(valuesOrLength); + default: + throw new DeveloperError('componentDatatype is not a valid enumeration value.'); + } + }; + + /** + * Creates a typed view of an array of bytes. + * @memberof ComponentDatatype + * + * @param {ComponentDatatype} componentDatatype The type of the view to create. + * @param {ArrayBuffer} buffer The buffer storage to use for the view. + * @param {Number} [byteOffset] The offset, in bytes, to the first element in the view. + * @param {Number} [length] The number of elements in the view. + * + * @returns {Int8Array|Uint8Array|Int16Array|Uint16Array|Float32Array|Float64Array} A typed array view of the buffer. + * + * @exception {DeveloperError} componentDatatype is required. + * @exception {DeveloperError} buffer is required. + * @exception {DeveloperError} componentDatatype is not a valid enumeration value. + */ + ComponentDatatype.createArrayBufferView = function(componentDatatype, buffer, byteOffset, length) { + if (!defined(componentDatatype)) { + throw new DeveloperError('componentDatatype is required.'); + } + + if (!defined(buffer)) { + throw new DeveloperError('buffer is required.'); + } + + byteOffset = defaultValue(byteOffset, 0); + length = defaultValue(length, (buffer.byteLength - byteOffset) / componentDatatype.sizeInBytes); + + switch (componentDatatype.value) { + case ComponentDatatype.BYTE.value: + return new Int8Array(buffer, byteOffset, length); + case ComponentDatatype.UNSIGNED_BYTE.value: + return new Uint8Array(buffer, byteOffset, length); + case ComponentDatatype.SHORT.value: + return new Int16Array(buffer, byteOffset, length); + case ComponentDatatype.UNSIGNED_SHORT.value: + return new Uint16Array(buffer, byteOffset, length); + case ComponentDatatype.FLOAT.value: + return new Float32Array(buffer, byteOffset, length); + case ComponentDatatype.DOUBLE.value: + return new Float64Array(buffer, byteOffset, length); + default: + throw new DeveloperError('componentDatatype is not a valid enumeration value.'); + } + }; + + return ComponentDatatype; +}); + +/*global define*/ +define('Core/IndexDatatype',[ + './defined', + './DeveloperError', + './Math' + ], function( + defined, + DeveloperError, + CesiumMath) { + "use strict"; + + /** + * Constants for WebGL index datatypes. These corresponds to the + * <code>type</code> parameter of <a href="http://www.khronos.org/opengles/sdk/docs/man/xhtml/glDrawElements.xml">drawElements</a>. + * + * @alias IndexDatatype + * @enumeration + */ + var IndexDatatype = { + /** + * 0x1401. 8-bit unsigned byte corresponding to <code>UNSIGNED_BYTE</code> and the type + * of an element in <code>Uint8Array</code>. + * + * @type {Number} + * @constant + */ + UNSIGNED_BYTE : 0x1401, + + /** + * 0x1403. 16-bit unsigned short corresponding to <code>UNSIGNED_SHORT</code> and the type + * of an element in <code>Uint16Array</code>. + * + * @type {Number} + * @constant + */ + UNSIGNED_SHORT : 0x1403, + + /** + * 0x1405. 32-bit unsigned int corresponding to <code>UNSIGNED_INT</code> and the type + * of an element in <code>Uint32Array</code>. + * + * @type {Number} + * @constant + */ + UNSIGNED_INT : 0x1405 + }; + + /** + * Returns the size, in bytes, of the corresponding datatype. + * + * @param {IndexDatatype} indexDatatype The index datatype to get the size of. + * + * @returns {Number} The size in bytes. + * + * @exception {DeveloperError} indexDatatype is required and must be a valid IndexDatatype constant. + * + * @example + * // Returns 2 + * var size = IndexDatatype.getSizeInBytes(IndexDatatype.UNSIGNED_SHORT); + */ + IndexDatatype.getSizeInBytes = function(indexDatatype) { + switch(indexDatatype) { + case IndexDatatype.UNSIGNED_BYTE: + return Uint8Array.BYTES_PER_ELEMENT; + case IndexDatatype.UNSIGNED_SHORT: + return Uint16Array.BYTES_PER_ELEMENT; + case IndexDatatype.UNSIGNED_INT: + return Uint32Array.BYTES_PER_ELEMENT; + } + + throw new DeveloperError('indexDatatype is required and must be a valid IndexDatatype constant.'); + }; + + /** + * Validates that the provided index datatype is a valid {@link IndexDatatype}. + * + * @param {IndexDatatype} indexDatatype The index datatype to validate. + * + * @returns {Boolean} <code>true</code> if the provided index datatype is a valid value; otherwise, <code>false</code>. + * + * @example + * if (!IndexDatatype.validate(indexDatatype)) { + * throw new DeveloperError('indexDatatype must be a valid value.'); + * } + */ + IndexDatatype.validate = function(indexDatatype) { + return defined(indexDatatype) && + (indexDatatype === IndexDatatype.UNSIGNED_BYTE || + indexDatatype === IndexDatatype.UNSIGNED_SHORT || + indexDatatype === IndexDatatype.UNSIGNED_INT); + }; + + /** + * Creates a typed array that will store indices, using either <code><Uint16Array</code> + * or <code>Uint32Array</code> depending on the number of vertices. + * + * @param {Number} numberOfVertices Number of vertices that the indices will reference. + * @param {Any} indicesLengthOrArray Passed through to the typed array constructor. + * + * @returns {Array} A <code>Uint16Array</code> or <code>Uint32Array</code> constructed with <code>indicesLengthOrArray</code>. + * + * @exception {DeveloperError} center is required. + * + * @example + * this.indices = IndexDatatype.createTypedArray(positions.length / 3, numberOfIndices); + */ + IndexDatatype.createTypedArray = function(numberOfVertices, indicesLengthOrArray) { + if (!defined(numberOfVertices)) { + throw new DeveloperError('numberOfVertices is required.'); + } + + if (numberOfVertices > CesiumMath.SIXTY_FOUR_KILOBYTES) { + return new Uint32Array(indicesLengthOrArray); + } + + return new Uint16Array(indicesLengthOrArray); + }; + + return IndexDatatype; +}); + +/*global define*/ +define('Core/Geometry',[ + './defaultValue', + './defined', + './DeveloperError', + './BoundingSphere' + ], function( + defaultValue, + defined, + DeveloperError, + BoundingSphere) { + "use strict"; + + /** + * A geometry representation with attributes forming vertices and optional index data + * defining primitives. Geometries and an {@link Appearance}, which describes the shading, + * can be assigned to a {@link Primitive} for visualization. A <code>Primitive</code> can + * be created from many heterogeneous - in many cases - geometries for performance. + * <p> + * In low-level rendering code, a vertex array can be created from a geometry using + * {@link Context#createVertexArrayFromGeometry}. + * </p> + * <p> + * Geometries can be transformed and optimized using functions in {@link GeometryPipeline}. + * </p> + * + * @alias Geometry + * @constructor + * + * @param {GeometryAttributes} options.attributes Attributes, which make up the geometry's vertices. + * @param {PrimitiveType} options.primitiveType The type of primitives in the geometry. + * @param {Array} [options.indices] Optional index data that determines the primitives in the geometry. + * @param {BoundingSphere} [options.boundingSphere] An optional bounding sphere that fully enclosed the geometry. + + * @exception {DeveloperError} options.attributes is required. + * @exception {DeveloperError} options.primitiveType is required. + * + * @example + * // Create geometry with a position attribute and indexed lines. + * var positions = new Float64Array([ + * 0.0, 0.0, 0.0, + * 7500000.0, 0.0, 0.0, + * 0.0, 7500000.0, 0.0 + * ]); + * + * var geometry = new Geometry({ + * attributes : { + * position : new GeometryAttribute({ + * componentDatatype : ComponentDatatype.DOUBLE, + * componentsPerAttribute : 3, + * values : positions + * }) + * }, + * indices : new Uint16Array([0, 1, 1, 2, 2, 0]), + * primitiveType : PrimitiveType.LINES, + * boundingSphere : BoundingSphere.fromVertices(positions) + * }); + * + * @demo <a href="http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Geometry%20and%20Appearances.html">Geometry and Appearances Demo</a> + * + * @see PolygonGeometry + * @see ExtentGeometry + * @see EllipseGeometry + * @see CircleGeometry + * @see WallGeometry + * @see SimplePolylineGeometry + * @see BoxGeometry + * @see EllipsoidGeometry + */ + var Geometry = function(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + + if (!defined(options.attributes)) { + throw new DeveloperError('options.attributes is required.'); + } + + if (!defined(options.primitiveType)) { + throw new DeveloperError('options.primitiveType is required.'); + } + + /** + * Attributes, which make up the geometry's vertices. Each property in this object corresponds to a + * {@link GeometryAttribute} containing the attribute's data. + * <p> + * Attributes are always stored non-interleaved in a Geometry. When geometry is prepared for rendering + * with {@link Context#createVertexArrayFromGeometry}, attributes are generally written interleaved + * into the vertex buffer for better rendering performance. + * </p> + * <p> + * There are reserved attribute names with well-known semantics. The following attributes + * are created by a Geometry (depending on the provided {@link VertexFormat}. + * <ul> + * <li><code>position</code> - 3D vertex position. 64-bit floating-point (for precision). 3 components per attribute. See {@link VertexFormat#position}.</li> + * <li><code>normal</code> - Normal (normalized), commonly used for lighting. 32-bit floating-point. 3 components per attribute. See {@link VertexFormat#normal}.</li> + * <li><code>st</code> - 2D texture coordinate. 32-bit floating-point. 2 components per attribute. See {@link VertexFormat#st}.</li> + * <li><code>binormal</code> - Binormal (normalized), used for tangent-space effects like bump mapping. 32-bit floating-point. 3 components per attribute. See {@link VertexFormat#binormal}.</li> + * <li><code>tangent</code> - Tangent (normalized), used for tangent-space effects like bump mapping. 32-bit floating-point. 3 components per attribute. See {@link VertexFormat#tangent}.</li> + * </ul> + * </p> + * <p> + * The following attribute names are generally not created by a Geometry, but are added + * to a Geometry by a {@link Primitive} or {@link GeometryPipeline} functions to prepare + * the geometry for rendering. + * <ul> + * <li><code>position3DHigh</code> - High 32 bits for encoded 64-bit position computed with {@link GeometryPipeline.encodeAttribute}. 32-bit floating-point. 4 components per attribute.</li> + * <li><code>position3DLow</code> - Low 32 bits for encoded 64-bit position computed with {@link GeometryPipeline.encodeAttribute}. 32-bit floating-point. 4 components per attribute.</li> + * <li><code>position3DHigh</code> - High 32 bits for encoded 64-bit 2D (Columbus view) position computed with {@link GeometryPipeline.encodeAttribute}. 32-bit floating-point. 4 components per attribute.</li> + * <li><code>position2DLow</code> - Low 32 bits for encoded 64-bit 2D (Columbus view) position computed with {@link GeometryPipeline.encodeAttribute}. 32-bit floating-point. 4 components per attribute.</li> + * <li><code>color</code> - RGBA color (normalized) usually from {@link GeometryInstance#color}. 32-bit floating-point. 4 components per attribute.</li> + * <li><code>pickColor</code> - RGBA color used for picking, created from {@link Context#createPickId}. 32-bit floating-point. 4 components per attribute.</li> + * </ul> + * </p> + * + * @type GeometryAttributes + * + * @default undefined + * + * @example + * geometry.attributes.position = new GeometryAttribute({ + * componentDatatype : ComponentDatatype.FLOAT, + * componentsPerAttribute : 3, + * values : new Float32Array() + * }); + * + * @see GeometryAttribute + * @see VertexFormat + */ + this.attributes = options.attributes; + + /** + * Optional index data that - along with {@link Geometry#primitiveType} - + * determines the primitives in the geometry. + * + * @type Array + * + * @default undefined + */ + this.indices = options.indices; + + /** + * The type of primitives in the geometry. This is most often {@link PrimitiveType.TRIANGLES}, + * but can varying based on the specific geometry. + * + * @type PrimitiveType + * + * @default undefined + */ + this.primitiveType = options.primitiveType; + + /** + * An optional bounding sphere that fully encloses the geometry. This is + * commonly used for culling. + * + * @type BoundingSphere + * + * @default undefined + */ + this.boundingSphere = options.boundingSphere; + }; + + /** + * Computes the number of vertices in a geometry. The runtime is linear with + * respect to the number of attributes in a vertex, not the number of vertices. + * + * @memberof Geometry + * + * @param {Cartesian3} geometry The geometry. + * + * @returns {Number} The number of vertices in the geometry. + * + * @exception {DeveloperError} geometries is required. + * + * @example + * var numVertices = Geometry.computeNumberOfVertices(geometry); + */ + Geometry.computeNumberOfVertices = function(geometry) { + if (!defined(geometry)) { + throw new DeveloperError('geometry is required.'); + } + + var numberOfVertices = -1; + for ( var property in geometry.attributes) { + if (geometry.attributes.hasOwnProperty(property) && + defined(geometry.attributes[property]) && + defined(geometry.attributes[property].values)) { + + var attribute = geometry.attributes[property]; + var num = attribute.values.length / attribute.componentsPerAttribute; + if ((numberOfVertices !== num) && (numberOfVertices !== -1)) { + throw new DeveloperError('All attribute lists must have the same number of attributes.'); + } + numberOfVertices = num; + } + } + + return numberOfVertices; + }; + + return Geometry; +}); + +/*global define*/ +define('Core/GeometryAttribute',[ + './defaultValue', + './defined', + './DeveloperError' + ], function( + defaultValue, + defined, + DeveloperError) { + "use strict"; + + /** + * Values and type information for geometry attributes. A {@link Geometry} + * generally contains one or more attributes. All attributes together form + * the geometry's vertices. + * + * @alias GeometryAttribute + * @constructor + * + * @param {ComponentDatatype} [options.componentDatatype=undefined] The datatype of each component in the attribute, e.g., individual elements in values. + * @param {Number} [options.componentsPerAttribute=undefined] A number between 1 and 4 that defines the number of components in an attributes. + * @param {Boolean} [options.normalize=false] When <code>true</code> and <code>componentDatatype</code> is an integer format, indicate that the components should be mapped to the range [0, 1] (unsigned) or [-1, 1] (signed) when they are accessed as floating-point for rendering. + * @param {Array} [options.values=undefined] The values for the attributes stored in a typed array. + * + * @exception {DeveloperError} options.componentDatatype is required. + * @exception {DeveloperError} options.componentsPerAttribute is required. + * @exception {DeveloperError} options.componentsPerAttribute must be between 1 and 4. + * @exception {DeveloperError} options.values is required. + * + * @example + * var geometry = new Geometry({ + * attributes : { + * position : new GeometryAttribute({ + * componentDatatype : ComponentDatatype.FLOAT, + * componentsPerAttribute : 3, + * values : [ + * 0.0, 0.0, 0.0, + * 7500000.0, 0.0, 0.0, + * 0.0, 7500000.0, 0.0 + * ] + * }) + * }, + * primitiveType : PrimitiveType.LINE_LOOP + * }); + * + * @see Geometry + */ + var GeometryAttribute = function(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + + if (!defined(options.componentDatatype)) { + throw new DeveloperError('options.componentDatatype is required.'); + } + + if (!defined(options.componentsPerAttribute)) { + throw new DeveloperError('options.componentsPerAttribute is required.'); + } + + if (options.componentsPerAttribute < 1 || options.componentsPerAttribute > 4) { + throw new DeveloperError('options.componentsPerAttribute must be between 1 and 4.'); + } + + if (!defined(options.values)) { + throw new DeveloperError('options.values is required.'); + } + + /** + * The datatype of each component in the attribute, e.g., individual elements in + * {@see GeometryAttribute#values}. + * + * @type ComponentDatatype + * + * @default undefined + */ + this.componentDatatype = options.componentDatatype; + + /** + * A number between 1 and 4 that defines the number of components in an attributes. + * For example, a position attribute with x, y, and z components would have 3 as + * shown in the code example. + * + * @type Number + * + * @default undefined + * + * @example + * attribute.componentDatatype : ComponentDatatype.FLOAT, + * attribute.componentsPerAttribute : 3, + * attribute.values = new Float32Array([ + * 0.0, 0.0, 0.0, + * 7500000.0, 0.0, 0.0, + * 0.0, 7500000.0, 0.0 + * ]); + */ + this.componentsPerAttribute = options.componentsPerAttribute; + + /** + * When <code>true</code> and <code>componentDatatype</code> is an integer format, + * indicate that the components should be mapped to the range [0, 1] (unsigned) + * or [-1, 1] (signed) when they are accessed as floating-point for rendering. + * <p> + * This is commonly used when storing colors using {@ ComponentDatatype.UNSIGNED_BYTE}. + * </p> + * + * @type Boolean + * + * @default false + * + * @example + * attribute.componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + * attribute.componentsPerAttribute : 4, + * attribute.normalize = true; + * attribute.values = new Uint8Array([ + * Color.floatToByte(color.red) + * Color.floatToByte(color.green) + * Color.floatToByte(color.blue) + * Color.floatToByte(color.alpha) + * ]); + */ + this.normalize = defaultValue(options.normalize, false); + + /** + * The values for the attributes stored in a typed array. In the code example, + * every three elements in <code>values</code> defines one attributes since + * <code>componentsPerAttribute</code> is 3. + * + * @type Array + * + * @default undefined + * + * @example + * attribute.componentDatatype : ComponentDatatype.FLOAT, + * attribute.componentsPerAttribute : 3, + * attribute.values = new Float32Array([ + * 0.0, 0.0, 0.0, + * 7500000.0, 0.0, 0.0, + * 0.0, 7500000.0, 0.0 + * ]); + */ + this.values = options.values; + }; + + return GeometryAttribute; +}); + +/*global define*/ +define('Core/GeometryAttributes',['./defaultValue'], function(defaultValue) { + "use strict"; + + /** + * Attributes, which make up a geometry's vertices. Each property in this object corresponds to a + * {@link GeometryAttribute} containing the attribute's data. + * <p> + * Attributes are always stored non-interleaved in a Geometry. When geometry is prepared for rendering + * with {@link Context#createVertexArrayFromGeometry}, attributes are generally written interleaved + * into the vertex buffer for better rendering performance. + * </p> + * + * @alias GeometryAttributes + * @constructor + */ + var GeometryAttributes = function(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + + /** + * The 3D position attribute. + * <p> + * 64-bit floating-point (for precision). 3 components per attribute. + * </p> + * + * @type GeometryAttribute + * + * @default undefined + */ + this.position = options.position; + + /** + * The normal attribute (normalized), which is commonly used for lighting. + * <p> + * 32-bit floating-point. 3 components per attribute. + * </p> + * + * @type GeometryAttribute + * + * @default undefined + */ + this.normal = options.normal; + + /** + * The 2D texture coordinate attribute. + * <p> + * 32-bit floating-point. 2 components per attribute + * </p> + * + * @type GeometryAttribute + * + * @default undefined + */ + this.st = options.st; + + /** + * The binormal attribute (normalized), which is used for tangent-space effects like bump mapping. + * <p> + * 32-bit floating-point. 3 components per attribute. + * </p> + * + * @type GeometryAttribute + * + * @default undefined + */ + this.binormal = options.binormal; + + /** + * The tangent attribute (normalized), which is used for tangent-space effects like bump mapping. + * <p> + * 32-bit floating-point. 3 components per attribute. + * </p> + * + * @type GeometryAttribute + * + * @default undefined + */ + this.tangent = options.tangent; + + /** + * The color attribute. + * <p> + * 8-bit unsigned integer. 4 components per attribute. + * </p> + * + * @type GeometryAttribute + * + * @default undefined + */ + this.color = options.color; + }; + + return GeometryAttributes; +}); +/*global define*/ +define('Core/Cartesian2',[ + './defaultValue', + './defined', + './DeveloperError', + './freezeObject' + ], function( + defaultValue, + defined, + DeveloperError, + freezeObject) { + "use strict"; + + /** + * A 2D Cartesian point. + * @alias Cartesian2 + * @constructor + * + * @param {Number} [x=0.0] The X component. + * @param {Number} [y=0.0] The Y component. + * + * @see Packable + * @see Cartesian3 + * @see Cartesian4 + */ + var Cartesian2 = function(x, y) { + /** + * The Y component. + * @type {Number} + * @default 0.0 + */ + this.x = defaultValue(x, 0.0); + + /** + * The X component. + * @type {Number} + * @default 0.0 + */ + this.y = defaultValue(y, 0.0); + }; + + /** + * Creates a Cartesian2 instance from x and y coordinates. + * @memberof Cartesian2 + * + * @param {Number} x The x coordinate. + * @param {Number} y The y coordinate. + * @param {Cartesian2} [result] The object onto which to store the result. + * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. + */ + Cartesian2.fromElements = function(x, y, result) { + if (!defined(result)) { + return new Cartesian2(x, y); + } + + result.x = x; + result.y = y; + return result; + }; + + /** + * Duplicates a Cartesian2 instance. + * @memberof Cartesian2 + * + * @param {Cartesian2} cartesian The Cartesian to duplicate. + * @param {Cartesian2} [result] The object onto which to store the result. + * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. (Returns undefined if cartesian is undefined) + */ + Cartesian2.clone = function(cartesian, result) { + if (!defined(cartesian)) { + return undefined; + } + + if (!defined(result)) { + return new Cartesian2(cartesian.x, cartesian.y); + } + + result.x = cartesian.x; + result.y = cartesian.y; + return result; + }; + + /** + * Creates a Cartesian2 instance from an existing Cartesian3. This simply takes the + * x and y properties of the Cartesian3 and drops z. + * @memberof Cartesian2 + * @function + * + * @param {Cartesian3} cartesian The Cartesian3 instance to create a Cartesian2 instance from. + * @param {Cartesian2} [result] The object onto which to store the result. + * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian2.fromCartesian3 = Cartesian2.clone; + + /** + * Creates a Cartesian2 instance from an existing Cartesian4. This simply takes the + * x and y properties of the Cartesian4 and drops z and w. + * @function + * + * @param {Cartesian4} cartesian The Cartesian4 instance to create a Cartesian2 instance from. + * @param {Cartesian2} [result] The object onto which to store the result. + * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian2.fromCartesian4 = Cartesian2.clone; + + /** + * The number of elements used to pack the object into an array. + * @Type {Number} + */ + Cartesian2.packedLength = 2; + + /** + * Stores the provided instance into the provided array. + * @memberof Cartesian2 + * + * @param {Cartesian2} value The value to pack. + * @param {Array} array The array to pack into. + * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. + * + * @exception {DeveloperError} value is required. + * @exception {DeveloperError} array is required. + */ + Cartesian2.pack = function(value, array, startingIndex) { + if (!defined(value)) { + throw new DeveloperError('value is required'); + } + + if (!defined(array)) { + throw new DeveloperError('array is required'); + } + + startingIndex = defaultValue(startingIndex, 0); + + array[startingIndex++] = value.x; + array[startingIndex] = value.y; + }; + + /** + * Retrieves an instance from a packed array. + * @memberof Cartesian2 + * + * @param {Array} array The packed array. + * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. + * @param {Cartesian2} [result] The object into which to store the result. + * + * @exception {DeveloperError} array is required. + */ + Cartesian2.unpack = function(array, startingIndex, result) { + if (!defined(array)) { + throw new DeveloperError('array is required'); + } + + startingIndex = defaultValue(startingIndex, 0); + + if (!defined(result)) { + result = new Cartesian2(); + } + result.x = array[startingIndex++]; + result.y = array[startingIndex]; + return result; + }; + + /** + * Creates a Cartesian2 from two consecutive elements in an array. + * @memberof Cartesian2 + * + * @param {Array} array The array whose two consecutive elements correspond to the x and y components, respectively. + * @param {Number} [startingIndex=0] The offset into the array of the first element, which corresponds to the x component. + * @param {Cartesian2} [result] The object onto which to store the result. + * + * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. + * + * @exception {DeveloperError} array is required. + * + * @example + * // Create a Cartesian2 with (1.0, 2.0) + * var v = [1.0, 2.0]; + * var p = Cartesian2.fromArray(v); + * + * // Create a Cartesian2 with (1.0, 2.0) using an offset into an array + * var v2 = [0.0, 0.0, 1.0, 2.0]; + * var p2 = Cartesian2.fromArray(v2, 2); + */ + Cartesian2.fromArray = Cartesian2.unpack; + + /** + * Computes the value of the maximum component for the supplied Cartesian. + * @memberof Cartesian2 + * + * @param {Cartesian2} The cartesian to use. + * @returns {Number} The value of the maximum component. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian2.getMaximumComponent = function(cartesian) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + return Math.max(cartesian.x, cartesian.y); + }; + + /** + * Computes the value of the minimum component for the supplied Cartesian. + * @memberof Cartesian2 + * + * @param {Cartesian2} The cartesian to use. + * @returns {Number} The value of the minimum component. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian2.getMinimumComponent = function(cartesian) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + return Math.min(cartesian.x, cartesian.y); + }; + + /** + * Compares two Cartesians and computes a Cartesian which contains the minimum components of the supplied Cartesians. + * @memberof Cartesian2 + * + * @param {Cartesian2} first A cartesian to compare. + * @param {Cartesian2} second A cartesian to compare. + * @param {Cartesian2} [result] The object into which to store the result. + * @returns {Cartesian2} A cartesian with the minimum components. + * + * @exception {DeveloperError} first is required. + * @exception {DeveloperError} second is required. + */ + Cartesian2.getMinimumByComponent = function(first, second, result) { + if (!defined(first)) { + throw new DeveloperError('first is required.'); + } + if (!defined(second)) { + throw new DeveloperError('second is required.'); + } + + if (!defined(result)) { + result = new Cartesian2(); + } + + result.x = Math.min(first.x, second.x); + result.y = Math.min(first.y, second.y); + + return result; + }; + + /** + * Compares two Cartesians and computes a Cartesian which contains the maximum components of the supplied Cartesians. + * @memberof Cartesian2 + * + * @param {Cartesian2} first A cartesian to compare. + * @param {Cartesian2} second A cartesian to compare. + * @param {Cartesian2} [result] The object into which to store the result. + * @returns {Cartesian2} A cartesian with the maximum components. + * + * @exception {DeveloperError} first is required. + * @exception {DeveloperError} second is required. + */ + Cartesian2.getMaximumByComponent = function(first, second, result) { + if (!defined(first)) { + throw new DeveloperError('first is required.'); + } + if (!defined(second)) { + throw new DeveloperError('second is required.'); + } + + if (!defined(result)) { + result = new Cartesian2(); + } + + result.x = Math.max(first.x, second.x); + result.y = Math.max(first.y, second.y); + return result; + }; + + /** + * Computes the provided Cartesian's squared magnitude. + * @memberof Cartesian2 + * + * @param {Cartesian2} cartesian The Cartesian instance whose squared magnitude is to be computed. + * @returns {Number} The squared magnitude. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian2.magnitudeSquared = function(cartesian) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + return cartesian.x * cartesian.x + cartesian.y * cartesian.y; + }; + + /** + * Computes the Cartesian's magnitude (length). + * @memberof Cartesian2 + * + * @param {Cartesian2} cartesian The Cartesian instance whose magnitude is to be computed. + * @returns {Number} The magnitude. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian2.magnitude = function(cartesian) { + return Math.sqrt(Cartesian2.magnitudeSquared(cartesian)); + }; + + var distanceScratch = new Cartesian2(); + + /** + * Computes the distance between two points + * @memberof Cartesian2 + * + * @param {Cartesian2} left The first point to compute the distance from. + * @param {Cartesian2} right The second point to compute the distance to. + * + * @returns {Number} The distance between two points. + * + * @exception {DeveloperError} left and right are required. + * + * @example + * // Returns 1.0 + * var d = Cartesian2.distance(new Cartesian2(1.0, 0.0), new Cartesian2(2.0, 0.0)); + */ + Cartesian2.distance = function(left, right) { + if (!defined(left) || !defined(right)) { + throw new DeveloperError('left and right are required.'); + } + + Cartesian2.subtract(left, right, distanceScratch); + return Cartesian2.magnitude(distanceScratch); + }; + + /** + * Computes the normalized form of the supplied Cartesian. + * @memberof Cartesian2 + * + * @param {Cartesian2} cartesian The Cartesian to be normalized. + * @param {Cartesian2} [result] The object onto which to store the result. + * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian2.normalize = function(cartesian, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + var magnitude = Cartesian2.magnitude(cartesian); + if (!defined(result)) { + return new Cartesian2(cartesian.x / magnitude, cartesian.y / magnitude); + } + result.x = cartesian.x / magnitude; + result.y = cartesian.y / magnitude; + return result; + }; + + /** + * Computes the dot (scalar) product of two Cartesians. + * @memberof Cartesian2 + * + * @param {Cartesian2} left The first Cartesian. + * @param {Cartesian2} right The second Cartesian. + * @returns {Number} The dot product. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + */ + Cartesian2.dot = function(left, right) { + if (!defined(left)) { + throw new DeveloperError('left is required'); + } + if (!defined(right)) { + throw new DeveloperError('right is required'); + } + + return left.x * right.x + left.y * right.y; + }; + + /** + * Computes the componentwise product of two Cartesians. + * @memberof Cartesian2 + * + * @param {Cartesian2} left The first Cartesian. + * @param {Cartesian2} right The second Cartesian. + * @param {Cartesian2} [result] The object onto which to store the result. + * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + */ + Cartesian2.multiplyComponents = function(left, right, result) { + if (!defined(left)) { + throw new DeveloperError('left is required'); + } + if (!defined(right)) { + throw new DeveloperError('right is required'); + } + + if (!defined(result)) { + return new Cartesian2(left.x * right.x, left.y * right.y); + } + result.x = left.x * right.x; + result.y = left.y * right.y; + return result; + }; + + /** + * Computes the componentwise sum of two Cartesians. + * @memberof Cartesian2 + * + * @param {Cartesian2} left The first Cartesian. + * @param {Cartesian2} right The second Cartesian. + * @param {Cartesian2} [result] The object onto which to store the result. + * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + */ + Cartesian2.add = function(left, right, result) { + if (!defined(left)) { + throw new DeveloperError('left is required'); + } + if (!defined(right)) { + throw new DeveloperError('right is required'); + } + + if (!defined(result)) { + return new Cartesian2(left.x + right.x, left.y + right.y); + } + result.x = left.x + right.x; + result.y = left.y + right.y; + return result; + }; + + /** + * Computes the componentwise difference of two Cartesians. + * @memberof Cartesian2 + * + * @param {Cartesian2} left The first Cartesian. + * @param {Cartesian2} right The second Cartesian. + * @param {Cartesian2} [result] The object onto which to store the result. + * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + */ + Cartesian2.subtract = function(left, right, result) { + if (!defined(left)) { + throw new DeveloperError('left is required'); + } + if (!defined(right)) { + throw new DeveloperError('right is required'); + } + + if (!defined(result)) { + return new Cartesian2(left.x - right.x, left.y - right.y); + } + result.x = left.x - right.x; + result.y = left.y - right.y; + return result; + }; + + /** + * Multiplies the provided Cartesian componentwise by the provided scalar. + * @memberof Cartesian2 + * + * @param {Cartesian2} cartesian The Cartesian to be scaled. + * @param {Number} scalar The scalar to multiply with. + * @param {Cartesian2} [result] The object onto which to store the result. + * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + * @exception {DeveloperError} scalar is required and must be a number. + */ + Cartesian2.multiplyByScalar = function(cartesian, scalar, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + if (typeof scalar !== 'number') { + throw new DeveloperError('scalar is required and must be a number.'); + } + + if (!defined(result)) { + return new Cartesian2(cartesian.x * scalar, cartesian.y * scalar); + } + result.x = cartesian.x * scalar; + result.y = cartesian.y * scalar; + return result; + }; + + /** + * Divides the provided Cartesian componentwise by the provided scalar. + * @memberof Cartesian2 + * + * @param {Cartesian2} cartesian The Cartesian to be divided. + * @param {Number} scalar The scalar to divide by. + * @param {Cartesian2} [result] The object onto which to store the result. + * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + * @exception {DeveloperError} scalar is required and must be a number. + */ + Cartesian2.divideByScalar = function(cartesian, scalar, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + if (typeof scalar !== 'number') { + throw new DeveloperError('scalar is required and must be a number.'); + } + + if (!defined(result)) { + return new Cartesian2(cartesian.x / scalar, cartesian.y / scalar); + } + result.x = cartesian.x / scalar; + result.y = cartesian.y / scalar; + return result; + }; + + /** + * Negates the provided Cartesian. + * @memberof Cartesian2 + * + * @param {Cartesian2} cartesian The Cartesian to be negated. + * @param {Cartesian2} [result] The object onto which to store the result. + * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian2.negate = function(cartesian, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + if (!defined(result)) { + return new Cartesian2(-cartesian.x, -cartesian.y); + } + result.x = -cartesian.x; + result.y = -cartesian.y; + return result; + }; + + /** + * Computes the absolute value of the provided Cartesian. + * @memberof Cartesian2 + * + * @param {Cartesian2} cartesian The Cartesian whose absolute value is to be computed. + * @param {Cartesian2} [result] The object onto which to store the result. + * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian2.abs = function(cartesian, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + if (!defined(result)) { + return new Cartesian2(Math.abs(cartesian.x), Math.abs(cartesian.y)); + } + result.x = Math.abs(cartesian.x); + result.y = Math.abs(cartesian.y); + return result; + }; + + var lerpScratch = new Cartesian2(); + /** + * Computes the linear interpolation or extrapolation at t using the provided cartesians. + * @memberof Cartesian2 + * + * @param start The value corresponding to t at 0.0. + * @param end The value corresponding to t at 1.0. + * @param t The point along t at which to interpolate. + * @param {Cartesian2} [result] The object onto which to store the result. + * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. + * + * @exception {DeveloperError} start is required. + * @exception {DeveloperError} end is required. + * @exception {DeveloperError} t is required and must be a number. + */ + Cartesian2.lerp = function(start, end, t, result) { + if (!defined(start)) { + throw new DeveloperError('start is required.'); + } + if (!defined(end)) { + throw new DeveloperError('end is required.'); + } + if (typeof t !== 'number') { + throw new DeveloperError('t is required and must be a number.'); + } + + Cartesian2.multiplyByScalar(end, t, lerpScratch); + result = Cartesian2.multiplyByScalar(start, 1.0 - t, result); + return Cartesian2.add(lerpScratch, result, result); + }; + + var angleBetweenScratch = new Cartesian2(); + var angleBetweenScratch2 = new Cartesian2(); + /** + * Returns the angle, in radians, between the provided Cartesians. + * @memberof Cartesian2 + * + * @param {Cartesian2} left The first Cartesian. + * @param {Cartesian2} right The second Cartesian. + * @returns {Number} The angle between the Cartesians. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + */ + Cartesian2.angleBetween = function(left, right) { + if (!defined(left)) { + throw new DeveloperError('left is required'); + } + if (!defined(right)) { + throw new DeveloperError('right is required'); + } + + Cartesian2.normalize(left, angleBetweenScratch); + Cartesian2.normalize(right, angleBetweenScratch2); + return Math.acos(Cartesian2.dot(angleBetweenScratch, angleBetweenScratch2)); + }; + + var mostOrthogonalAxisScratch = new Cartesian2(); + /** + * Returns the axis that is most orthogonal to the provided Cartesian. + * @memberof Cartesian2 + * + * @param {Cartesian2} cartesian The Cartesian on which to find the most orthogonal axis. + * @param {Cartesian2} [result] The object onto which to store the result. + * @returns {Cartesian2} The most orthogonal axis. + * + * @exception {DeveloperError} cartesian is required. + */ + Cartesian2.mostOrthogonalAxis = function(cartesian, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required.'); + } + + var f = Cartesian2.normalize(cartesian, mostOrthogonalAxisScratch); + Cartesian2.abs(f, f); + + if (f.x <= f.y) { + result = Cartesian2.clone(Cartesian2.UNIT_X, result); + } else { + result = Cartesian2.clone(Cartesian2.UNIT_Y, result); + } + + return result; + }; + + /** + * Compares the provided Cartesians componentwise and returns + * <code>true</code> if they are equal, <code>false</code> otherwise. + * @memberof Cartesian2 + * + * @param {Cartesian2} [left] The first Cartesian. + * @param {Cartesian2} [right] The second Cartesian. + * @returns {Boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise. + */ + Cartesian2.equals = function(left, right) { + return (left === right) || + ((defined(left)) && + (defined(right)) && + (left.x === right.x) && + (left.y === right.y)); + }; + + /** + * Compares the provided Cartesians componentwise and returns + * <code>true</code> if they are within the provided epsilon, + * <code>false</code> otherwise. + * @memberof Cartesian2 + * + * @param {Cartesian2} [left] The first Cartesian. + * @param {Cartesian2} [right] The second Cartesian. + * @param {Number} epsilon The epsilon to use for equality testing. + * @returns {Boolean} <code>true</code> if left and right are within the provided epsilon, <code>false</code> otherwise. + * + * @exception {DeveloperError} epsilon is required and must be a number. + */ + Cartesian2.equalsEpsilon = function(left, right, epsilon) { + if (typeof epsilon !== 'number') { + throw new DeveloperError('epsilon is required and must be a number.'); + } + + return (left === right) || + ((defined(left)) && + (defined(right)) && + (Math.abs(left.x - right.x) <= epsilon) && + (Math.abs(left.y - right.y) <= epsilon)); + }; + + /** + * An immutable Cartesian2 instance initialized to (0.0, 0.0). + * @memberof Cartesian2 + */ + Cartesian2.ZERO = freezeObject(new Cartesian2(0.0, 0.0)); + + /** + * An immutable Cartesian2 instance initialized to (1.0, 0.0). + * @memberof Cartesian2 + */ + Cartesian2.UNIT_X = freezeObject(new Cartesian2(1.0, 0.0)); + + /** + * An immutable Cartesian2 instance initialized to (0.0, 1.0). + * @memberof Cartesian2 + */ + Cartesian2.UNIT_Y = freezeObject(new Cartesian2(0.0, 1.0)); + + /** + * Duplicates this Cartesian2 instance. + * @memberof Cartesian2 + * + * @param {Cartesian2} [result] The object onto which to store the result. + * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. + */ + Cartesian2.prototype.clone = function(result) { + return Cartesian2.clone(this, result); + }; + + /** + * Compares this Cartesian against the provided Cartesian componentwise and returns + * <code>true</code> if they are equal, <code>false</code> otherwise. + * @memberof Cartesian2 + * + * @param {Cartesian2} [right] The right hand side Cartesian. + * @returns {Boolean} <code>true</code> if they are equal, <code>false</code> otherwise. + */ + Cartesian2.prototype.equals = function(right) { + return Cartesian2.equals(this, right); + }; + + /** + * Compares this Cartesian against the provided Cartesian componentwise and returns + * <code>true</code> if they are within the provided epsilon, + * <code>false</code> otherwise. + * @memberof Cartesian2 + * + * @param {Cartesian2} [right] The right hand side Cartesian. + * @param {Number} epsilon The epsilon to use for equality testing. + * @returns {Boolean} <code>true</code> if they are within the provided epsilon, <code>false</code> otherwise. + * + * @exception {DeveloperError} epsilon is required and must be a number. + */ + Cartesian2.prototype.equalsEpsilon = function(right, epsilon) { + return Cartesian2.equalsEpsilon(this, right, epsilon); + }; + + /** + * Creates a string representing this Cartesian in the format '(x, y)'. + * @memberof Cartesian2 + * + * @returns {String} A string representing the provided Cartesian in the format '(x, y)'. + */ + Cartesian2.prototype.toString = function() { + return '(' + this.x + ', ' + this.y + ')'; + }; + + return Cartesian2; +}); + +/*global define*/ +define('Core/Matrix2',[ + './Cartesian2', + './defaultValue', + './defined', + './DeveloperError', + './freezeObject' + ], function( + Cartesian2, + defaultValue, + defined, + DeveloperError, + freezeObject) { + "use strict"; + + /** + * A 2x2 matrix, indexable as a column-major order array. + * Constructor parameters are in row-major order for code readability. + * @alias Matrix2 + * @constructor + * + * @param {Number} [column0Row0=0.0] The value for column 0, row 0. + * @param {Number} [column1Row0=0.0] The value for column 1, row 0. + * @param {Number} [column0Row1=0.0] The value for column 0, row 1. + * @param {Number} [column1Row1=0.0] The value for column 1, row 1. + * + * @see Matrix2.fromColumnMajor + * @see Matrix2.fromRowMajorArray + * @see Matrix2.fromScale + * @see Matrix2.fromUniformScale + * @see Matrix3 + * @see Matrix4 + */ + var Matrix2 = function(column0Row0, column1Row0, column0Row1, column1Row1) { + this[0] = defaultValue(column0Row0, 0.0); + this[1] = defaultValue(column0Row1, 0.0); + this[2] = defaultValue(column1Row0, 0.0); + this[3] = defaultValue(column1Row1, 0.0); + }; + + /** + * Duplicates a Matrix2 instance. + * @memberof Matrix2 + * + * @param {Matrix2} matrix The matrix to duplicate. + * @param {Matrix2} [result] The object onto which to store the result. + * @returns {Matrix2} The modified result parameter or a new Matrix2 instance if one was not provided. (Returns undefined if matrix is undefined) + */ + Matrix2.clone = function(values, result) { + if (!defined(values)) { + return undefined; + } + if (!defined(result)) { + return new Matrix2(values[0], values[2], + values[1], values[3]); + } + result[0] = values[0]; + result[1] = values[1]; + result[2] = values[2]; + result[3] = values[3]; + return result; + }; + + /** + * Creates a Matrix2 from 4 consecutive elements in an array. + * @memberof Matrix2 + * + * @param {Array} array The array whose 4 consecutive elements correspond to the positions of the matrix. Assumes column-major order. + * @param {Number} [startingIndex=0] The offset into the array of the first element, which corresponds to first column first row position in the matrix. + * @param {Matrix2} [result] The object onto which to store the result. + * + * @returns {Matrix2} The modified result parameter or a new Matrix2 instance if one was not provided. + * + * @exception {DeveloperError} array is required. + * + * @example + * // Create the Matrix2: + * // [1.0, 2.0] + * // [1.0, 2.0] + * + * var v = [1.0, 1.0, 2.0, 2.0]; + * var m = Matrix2.fromArray(v); + * + * // Create same Matrix2 with using an offset into an array + * var v2 = [0.0, 0.0, 1.0, 1.0, 2.0, 2.0]; + * var m2 = Matrix2.fromArray(v2, 2); + */ + Matrix2.fromArray = function(array, startingIndex, result) { + if (!defined(array)) { + throw new DeveloperError('array is required'); + } + + startingIndex = defaultValue(startingIndex, 0); + + if (!defined(result)) { + result = new Matrix2(); + } + + result[0] = array[startingIndex]; + result[1] = array[startingIndex + 1]; + result[2] = array[startingIndex + 2]; + result[3] = array[startingIndex + 3]; + return result; + }; + + /** + * Creates a Matrix2 instance from a column-major order array. + * @memberof Matrix2 + * @function + * + * @param {Array} values The column-major order array. + * @param {Matrix2} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix2 instance if one was not provided. + * + * @exception {DeveloperError} values is required. + */ + Matrix2.fromColumnMajorArray = function(values, result) { + if (!defined(values)) { + throw new DeveloperError('values parameter is required'); + } + + return Matrix2.clone(values, result); + }; + + /** + * Creates a Matrix2 instance from a row-major order array. + * The resulting matrix will be in column-major order. + * @memberof Matrix2 + * + * @param {Array} values The row-major order array. + * @param {Matrix2} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix2 instance if one was not provided. + * + * @exception {DeveloperError} values is required. + */ + Matrix2.fromRowMajorArray = function(values, result) { + if (!defined(values)) { + throw new DeveloperError('values is required.'); + } + + if (!defined(result)) { + return new Matrix2(values[0], values[1], + values[2], values[3]); + } + result[0] = values[0]; + result[1] = values[2]; + result[2] = values[1]; + result[3] = values[3]; + return result; + }; + + /** + * Computes a Matrix2 instance representing a non-uniform scale. + * @memberof Matrix2 + * + * @param {Cartesian2} scale The x and y scale factors. + * @param {Matrix2} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix2 instance if one was not provided. + * + * @exception {DeveloperError} scale is required. + * + * @example + * // Creates + * // [7.0, 0.0] + * // [0.0, 8.0] + * var m = Matrix2.fromScale(new Cartesian2(7.0, 8.0)); + */ + Matrix2.fromScale = function(scale, result) { + if (!defined(scale)) { + throw new DeveloperError('scale is required.'); + } + + if (!defined(result)) { + return new Matrix2( + scale.x, 0.0, + 0.0, scale.y); + } + + result[0] = scale.x; + result[1] = 0.0; + result[2] = 0.0; + result[3] = scale.y; + return result; + }; + + /** + * Computes a Matrix2 instance representing a uniform scale. + * @memberof Matrix2 + * + * @param {Number} scale The uniform scale factor. + * @param {Matrix2} [result] The object in which the result will be stored, if undefined a new instance will be created. + * @returns The modified result parameter, or a new Matrix2 instance if one was not provided. + * + * @exception {DeveloperError} scale is required. + * + * @example + * // Creates + * // [2.0, 0.0] + * // [0.0, 2.0] + * var m = Matrix2.fromUniformScale(2.0); + */ + Matrix2.fromUniformScale = function(scale, result) { + if (typeof scale !== 'number') { + throw new DeveloperError('scale is required.'); + } + + if (!defined(result)) { + return new Matrix2( + scale, 0.0, + 0.0, scale); + } + + result[0] = scale; + result[1] = 0.0; + result[2] = 0.0; + result[3] = scale; + return result; + }; + + /** + * Creates a rotation matrix. + * + * @param {Number} angle The angle, in radians, of the rotation. Positive angles are counterclockwise. + * @param {Matrix2} [result] The object in which the result will be stored, if undefined a new instance will be created. + * + * @returns The modified result parameter, or a new Matrix2 instance if one was not provided. + * + * @exception {DeveloperError} angle is required. + * + * @example + * // Rotate a point 45 degrees counterclockwise. + * var p = new Cartesian2(5, 6); + * var m = Matrix2.fromRotation(CesiumMath.toRadians(45.0)); + * var rotated = Matrix2.multiplyByVector(m, p); + */ + Matrix2.fromRotation = function(angle, result) { + if (!defined(angle)) { + throw new DeveloperError('angle is required.'); + } + + var cosAngle = Math.cos(angle); + var sinAngle = Math.sin(angle); + + if (!defined(result)) { + return new Matrix2( + cosAngle, -sinAngle, + sinAngle, cosAngle); + } + result[0] = cosAngle; + result[1] = sinAngle; + result[2] = -sinAngle; + result[3] = cosAngle; + return result; + }; + + /** + * Creates an Array from the provided Matrix2 instance. + * The array will be in column-major order. + * @memberof Matrix2 + * + * @param {Matrix2} matrix The matrix to use.. + * @param {Array} [result] The Array onto which to store the result. + * @returns {Array} The modified Array parameter or a new Array instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + */ + Matrix2.toArray = function(matrix, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + + if (!defined(result)) { + return [matrix[0], matrix[1], matrix[2], matrix[3]]; + } + result[0] = matrix[0]; + result[1] = matrix[1]; + result[2] = matrix[2]; + result[3] = matrix[3]; + return result; + }; + + /** + * Computes the array index of the element at the provided row and column. + * @memberof Matrix2 + * + * @param {Number} row The zero-based index of the row. + * @param {Number} column The zero-based index of the column. + * @returns {Number} The index of the element at the provided row and column. + * + * @exception {DeveloperError} row is required and must be 0 or 1. + * @exception {DeveloperError} column is required and must be 0 or 1. + * + * @example + * var myMatrix = new Matrix2(); + * var column1Row0Index = Matrix2.getElementIndex(1, 0); + * var column1Row0 = myMatrix[column1Row0Index] + * myMatrix[column1Row0Index] = 10.0; + */ + Matrix2.getElementIndex = function(column, row) { + if (typeof row !== 'number' || row < 0 || row > 1) { + throw new DeveloperError('row is required and must be 0 or 1.'); + } + if (typeof column !== 'number' || column < 0 || column > 1) { + throw new DeveloperError('column is required and must be 0 or 1.'); + } + + return column * 2 + row; + }; + + /** + * Retrieves a copy of the matrix column at the provided index as a Cartesian2 instance. + * @memberof Matrix2 + * + * @param {Matrix2} matrix The matrix to use. + * @param {Number} index The zero-based index of the column to retrieve. + * @param {Cartesian2} [result] The object onto which to store the result. + * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} index is required and must be 0 or 1. + * + * @see Cartesian2 + */ + Matrix2.getColumn = function(matrix, index, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required.'); + } + + if (typeof index !== 'number' || index < 0 || index > 1) { + throw new DeveloperError('index is required and must be 0 or 1.'); + } + + var startIndex = index * 2; + var x = matrix[startIndex]; + var y = matrix[startIndex + 1]; + + if (!defined(result)) { + return new Cartesian2(x, y); + } + result.x = x; + result.y = y; + return result; + }; + + /** + * Computes a new matrix that replaces the specified column in the provided matrix with the provided Cartesian2 instance. + * @memberof Matrix2 + * + * @param {Matrix2} matrix The matrix to use. + * @param {Number} index The zero-based index of the column to set. + * @param {Cartesian2} cartesian The Cartesian whose values will be assigned to the specified column. + * @param {Cartesian2} [result] The object onto which to store the result. + * @returns {Matrix2} The modified result parameter or a new Matrix2 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} cartesian is required. + * @exception {DeveloperError} index is required and must be 0 or 1. + * + * @see Cartesian2 + */ + Matrix2.setColumn = function(matrix, index, cartesian, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + if (typeof index !== 'number' || index < 0 || index > 1) { + throw new DeveloperError('index is required and must be 0 or 1.'); + } + + result = Matrix2.clone(matrix, result); + var startIndex = index * 2; + result[startIndex] = cartesian.x; + result[startIndex + 1] = cartesian.y; + return result; + }; + + /** + * Retrieves a copy of the matrix row at the provided index as a Cartesian2 instance. + * @memberof Matrix2 + * + * @param {Matrix2} matrix The matrix to use. + * @param {Number} index The zero-based index of the row to retrieve. + * @param {Cartesian2} [result] The object onto which to store the result. + * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} index is required and must be 0 or 1. + * + * @see Cartesian2 + */ + Matrix2.getRow = function(matrix, index, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required.'); + } + + if (typeof index !== 'number' || index < 0 || index > 1) { + throw new DeveloperError('index is required and must be 0 or 1.'); + } + + var x = matrix[index]; + var y = matrix[index + 2]; + + if (!defined(result)) { + return new Cartesian2(x, y); + } + result.x = x; + result.y = y; + return result; + }; + + /** + * Computes a new matrix that replaces the specified row in the provided matrix with the provided Cartesian2 instance. + * @memberof Matrix2 + * + * @param {Matrix2} matrix The matrix to use. + * @param {Number} index The zero-based index of the row to set. + * @param {Cartesian2} cartesian The Cartesian whose values will be assigned to the specified row. + * @param {Cartesian2} [result] The object onto which to store the result. + * @returns {Matrix2} The modified result parameter or a new Matrix2 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} cartesian is required. + * @exception {DeveloperError} index is required and must be 0 or 1. + * + * @see Cartesian2 + */ + Matrix2.setRow = function(matrix, index, cartesian, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + if (typeof index !== 'number' || index < 0 || index > 1) { + throw new DeveloperError('index is required and must be 0 or 1.'); + } + + result = Matrix2.clone(matrix, result); + result[index] = cartesian.x; + result[index + 2] = cartesian.y; + return result; + }; + + /** + * Computes the product of two matrices. + * @memberof Matrix2 + * + * @param {Matrix2} left The first matrix. + * @param {Matrix2} right The second matrix. + * @param {Matrix2} [result] The object onto which to store the result. + * @returns {Matrix2} The modified result parameter or a new Matrix2 instance if one was not provided. + * + * @exception {DeveloperError} left is required. + * @exception {DeveloperError} right is required. + */ + Matrix2.multiply = function(left, right, result) { + if (!defined(left)) { + throw new DeveloperError('left is required'); + } + if (!defined(right)) { + throw new DeveloperError('right is required'); + } + + var column0Row0 = left[0] * right[0] + left[2] * right[1]; + var column1Row0 = left[0] * right[2] + left[2] * right[3]; + var column0Row1 = left[1] * right[0] + left[3] * right[1]; + var column1Row1 = left[1] * right[2] + left[3] * right[3]; + + if (!defined(result)) { + return new Matrix2(column0Row0, column1Row0, + column0Row1, column1Row1); + } + result[0] = column0Row0; + result[1] = column0Row1; + result[2] = column1Row0; + result[3] = column1Row1; + return result; + }; + + /** + * Computes the product of a matrix and a column vector. + * @memberof Matrix2 + * + * @param {Matrix2} matrix The matrix. + * @param {Cartesian2} cartesian The column. + * @param {Cartesian2} [result] The object onto which to store the result. + * @returns {Cartesian2} The modified result parameter or a new Cartesian2 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} cartesian is required. + */ + Matrix2.multiplyByVector = function(matrix, cartesian, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + var x = matrix[0] * cartesian.x + matrix[2] * cartesian.y; + var y = matrix[1] * cartesian.x + matrix[3] * cartesian.y; + + if (!defined(result)) { + return new Cartesian2(x, y); + } + result.x = x; + result.y = y; + return result; + }; + + /** + * Computes the product of a matrix and a scalar. + * @memberof Matrix2 + * + * @param {Matrix2} matrix The matrix. + * @param {Number} scalar The number to multiply by. + * @param {Matrix2} [result] The object onto which to store the result. + * @returns {Matrix2} The modified result parameter or a new Cartesian2 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + * @exception {DeveloperError} scalar is required and must be a number. + */ + Matrix2.multiplyByScalar = function(matrix, scalar, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + if (typeof scalar !== 'number') { + throw new DeveloperError('scalar is required and must be a number'); + } + + if (!defined(result)) { + return new Matrix2(matrix[0] * scalar, matrix[2] * scalar, + matrix[1] * scalar, matrix[3] * scalar); + } + result[0] = matrix[0] * scalar; + result[1] = matrix[1] * scalar; + result[2] = matrix[2] * scalar; + result[3] = matrix[3] * scalar; + return result; + }; + + /** + * Creates a negated copy of the provided matrix. + * @memberof Matrix2 + * + * @param {Matrix2} matrix The matrix to negate. + * @param {Matrix2} [result] The object onto which to store the result. + * @returns {Matrix2} The modified result parameter or a new Matrix2 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + */ + Matrix2.negate = function(matrix, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + + if (!defined(result)) { + return new Matrix2(-matrix[0], -matrix[2], + -matrix[1], -matrix[3]); + } + result[0] = -matrix[0]; + result[1] = -matrix[1]; + result[2] = -matrix[2]; + result[3] = -matrix[3]; + return result; + }; + + /** + * Computes the transpose of the provided matrix. + * @memberof Matrix2 + * + * @param {Matrix2} matrix The matrix to transpose. + * @param {Matrix2} [result] The object onto which to store the result. + * @returns {Matrix2} The modified result parameter or a new Matrix2 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + */ + Matrix2.transpose = function(matrix, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + + var column0Row0 = matrix[0]; + var column0Row1 = matrix[2]; + var column1Row0 = matrix[1]; + var column1Row1 = matrix[3]; + + if (!defined(result)) { + return new Matrix2(column0Row0, column1Row0, + column0Row1, column1Row1); + } + result[0] = column0Row0; + result[1] = column0Row1; + result[2] = column1Row0; + result[3] = column1Row1; + return result; + }; + + /** + * Computes a matrix, which contains the absolute (unsigned) values of the provided matrix's elements. + * @memberof Matrix2 + * + * @param {Matrix2} matrix The matrix with signed elements. + * @param {Matrix2} [result] The object onto which to store the result. + * @returns {Matrix2} The modified result parameter or a new Matrix2 instance if one was not provided. + * + * @exception {DeveloperError} matrix is required. + */ + Matrix2.abs = function(matrix, result) { + if (!defined(matrix)) { + throw new DeveloperError('matrix is required'); + } + + if (!defined(result)) { + return new Matrix2(Math.abs(matrix[0]), Math.abs(matrix[2]), + Math.abs(matrix[1]), Math.abs(matrix[3])); + } + result[0] = Math.abs(matrix[0]); + result[1] = Math.abs(matrix[1]); + result[2] = Math.abs(matrix[2]); + result[3] = Math.abs(matrix[3]); + + return result; + }; + + /** + * Compares the provided matrices componentwise and returns + * <code>true</code> if they are equal, <code>false</code> otherwise. + * @memberof Matrix2 + * + * @param {Matrix2} [left] The first matrix. + * @param {Matrix2} [right] The second matrix. + * @returns {Boolean} <code>true</code> if left and right are equal, <code>false</code> otherwise. + */ + Matrix2.equals = function(left, right) { + return (left === right) || + (defined(left) && + defined(right) && + left[0] === right[0] && + left[1] === right[1] && + left[2] === right[2] && + left[3] === right[3]); + }; + + /** + * Compares the provided matrices componentwise and returns + * <code>true</code> if they are within the provided epsilon, + * <code>false</code> otherwise. + * @memberof Matrix2 + * + * @param {Matrix2} [left] The first matrix. + * @param {Matrix2} [right] The second matrix. + * @param {Number} epsilon The epsilon to use for equality testing. + * @returns {Boolean} <code>true</code> if left and right are within the provided epsilon, <code>false</code> otherwise. + * + * @exception {DeveloperError} epsilon is required and must be a number. + */ + Matrix2.equalsEpsilon = function(left, right, epsilon) { + if (typeof epsilon !== 'number') { + throw new DeveloperError('epsilon is required and must be a number'); + } + + return (left === right) || + (defined(left) && + defined(right) && + Math.abs(left[0] - right[0]) <= epsilon && + Math.abs(left[1] - right[1]) <= epsilon && + Math.abs(left[2] - right[2]) <= epsilon && + Math.abs(left[3] - right[3]) <= epsilon); + }; + + /** + * An immutable Matrix2 instance initialized to the identity matrix. + * @memberof Matrix2 + */ + Matrix2.IDENTITY = freezeObject(new Matrix2(1.0, 0.0, + 0.0, 1.0)); + + /** + * The index into Matrix2 for column 0, row 0. + * @memberof Matrix2 + * + * @example + * var matrix = new Matrix2(); + * matrix[Matrix2.COLUMN0ROW0] = 5.0; //set column 0, row 0 to 5.0 + */ + Matrix2.COLUMN0ROW0 = 0; + + /** + * The index into Matrix2 for column 0, row 1. + * @memberof Matrix2 + * + * @example + * var matrix = new Matrix2(); + * matrix[Matrix2.COLUMN0ROW1] = 5.0; //set column 0, row 1 to 5.0 + */ + Matrix2.COLUMN0ROW1 = 1; + + /** + * The index into Matrix2 for column 1, row 0. + * @memberof Matrix2 + * + * @example + * var matrix = new Matrix2(); + * matrix[Matrix2.COLUMN1ROW0] = 5.0; //set column 1, row 0 to 5.0 + */ + Matrix2.COLUMN1ROW0 = 2; + + /** + * The index into Matrix2 for column 1, row 1. + * @memberof Matrix2 + * + * @example + * var matrix = new Matrix2(); + * matrix[Matrix2.COLUMN1ROW1] = 5.0; //set column 1, row 1 to 5.0 + */ + Matrix2.COLUMN1ROW1 = 3; + + /** + * Duplicates the provided Matrix2 instance. + * @memberof Matrix2 + * + * @param {Matrix2} [result] The object onto which to store the result. + * @returns {Matrix2} The modified result parameter or a new Matrix2 instance if one was not provided. + */ + Matrix2.prototype.clone = function(result) { + return Matrix2.clone(this, result); + }; + + /** + * Compares this matrix to the provided matrix componentwise and returns + * <code>true</code> if they are equal, <code>false</code> otherwise. + * @memberof Matrix2 + * + * @param {Matrix2} [right] The right hand side matrix. + * @returns {Boolean} <code>true</code> if they are equal, <code>false</code> otherwise. + */ + Matrix2.prototype.equals = function(right) { + return Matrix2.equals(this, right); + }; + + /** + * Compares this matrix to the provided matrix componentwise and returns + * <code>true</code> if they are within the provided epsilon, + * <code>false</code> otherwise. + * @memberof Matrix2 + * + * @param {Matrix2} [right] The right hand side matrix. + * @param {Number} epsilon The epsilon to use for equality testing. + * @returns {Boolean} <code>true</code> if they are within the provided epsilon, <code>false</code> otherwise. + * + * @exception {DeveloperError} epsilon is required and must be a number. + */ + Matrix2.prototype.equalsEpsilon = function(right, epsilon) { + return Matrix2.equalsEpsilon(this, right, epsilon); + }; + + /** + * Creates a string representing this Matrix with each row being + * on a separate line and in the format '(column0, column1)'. + * @memberof Matrix2 + * + * @returns {String} A string representing the provided Matrix with each row being on a separate line and in the format '(column0, column1)'. + */ + Matrix2.prototype.toString = function() { + return '(' + this[0] + ', ' + this[2] + ')\n' + + '(' + this[1] + ', ' + this[3] + ')'; + }; + + return Matrix2; +}); + +/*global define*/ +define('Core/PrimitiveType',[],function() { + "use strict"; + + /** + * DOC_TBA + * + * @exports PrimitiveType + */ + var PrimitiveType = { + /** + * 0x0000. Points primitive where each vertex (or index) is a separate point. + * + * @type {Number} + * @constant + */ + POINTS : 0x0000, + /** + * 0x0001. Lines primitive where each two vertices (or indices) is a line segment. Line segments are not necessarily connected. + * + * @type {Number} + * @constant + */ + LINES : 0x0001, + /** + * 0x0002. Line loop primitive where each vertex (or index) after the first connects a line to + * the previous vertex, and the last vertex implicitly connects to the first. + * + * @type {Number} + * @constant + */ + LINE_LOOP : 0x0002, + /** + * 0x0003. Line strip primitive where each vertex (or index) after the first connects a line to the previous vertex. + * + * @type {Number} + * @constant + */ + LINE_STRIP : 0x0003, + /** + * 0x0004. Triangles primitive where each three vertices (or indices) is a triangle. Triangles do not necessarily share edges. + * + * @type {Number} + * @constant + */ + TRIANGLES : 0x0004, + /** + * 0x0005. Triangle strip primitive where each vertex (or index) after the first two connect to + * the previous two vertices forming a triangle. For example, this can be used to model a wall. + * + * @type {Number} + * @constant + */ + TRIANGLE_STRIP : 0x0005, + /** + * 0x0006. Triangle fan primitive where each vertex (or index) after the first two connect to + * the previous vertex and the first vertex forming a triangle. For example, this can be used + * to model a cone or circle. + * + * @type {Number} + * @constant + */ + TRIANGLE_FAN : 0x0006, + + /** + * DOC_TBA + * + * @param {PrimitiveType} primitiveType + * + * @returns {Boolean} + */ + validate : function(primitiveType) { + return ((primitiveType === PrimitiveType.POINTS) || + (primitiveType === PrimitiveType.LINES) || + (primitiveType === PrimitiveType.LINE_LOOP) || + (primitiveType === PrimitiveType.LINE_STRIP) || + (primitiveType === PrimitiveType.TRIANGLES) || + (primitiveType === PrimitiveType.TRIANGLE_STRIP) || + (primitiveType === PrimitiveType.TRIANGLE_FAN)); + } + }; + + return PrimitiveType; +}); + +/*global define*/ +define('Core/ExtentOutlineGeometry',[ + './defaultValue', + './defined', + './BoundingSphere', + './Cartesian3', + './Cartographic', + './ComponentDatatype', + './IndexDatatype', + './DeveloperError', + './Ellipsoid', + './GeographicProjection', + './Geometry', + './GeometryAttribute', + './GeometryAttributes', + './Math', + './Matrix2', + './PrimitiveType' + ], function( + defaultValue, + defined, + BoundingSphere, + Cartesian3, + Cartographic, + ComponentDatatype, + IndexDatatype, + DeveloperError, + Ellipsoid, + GeographicProjection, + Geometry, + GeometryAttribute, + GeometryAttributes, + CesiumMath, + Matrix2, + PrimitiveType) { + "use strict"; + + function isValidLatLon(latitude, longitude) { + if (latitude < -CesiumMath.PI_OVER_TWO || latitude > CesiumMath.PI_OVER_TWO) { + return false; + } + if (longitude > CesiumMath.PI || longitude < -CesiumMath.PI) { + return false; + } + return true; + } + + var nw = new Cartesian3(); + var nwCartographic = new Cartographic(); + var centerCartographic = new Cartographic(); + var center = new Cartesian3(); + var rotationMatrix = new Matrix2(); + var proj = new GeographicProjection(); + var position = new Cartesian3(); + var extrudedPosition = new Cartesian3(); + var bottomBoundingSphere = new BoundingSphere(); + var topBoundingSphere = new BoundingSphere(); + + var cos = Math.cos; + var sin = Math.sin; + var sqrt = Math.sqrt; + + var stLatitude, stLongitude; + + function computePosition(params, row, col, maxHeight, minHeight) { + var radiiSquared = params.radiiSquared; + + stLatitude = nwCartographic.latitude - params.granYCos * row + col * params.granXSin; + var cosLatitude = cos(stLatitude); + var nZ = sin(stLatitude); + var kZ = radiiSquared.z * nZ; + + stLongitude = nwCartographic.longitude + row * params.granYSin + col * params.granXCos; + var nX = cosLatitude * cos(stLongitude); + var nY = cosLatitude * sin(stLongitude); + + var kX = radiiSquared.x * nX; + var kY = radiiSquared.y * nY; + + var gamma = sqrt((kX * nX) + (kY * nY) + (kZ * nZ)); + + var rSurfaceX = kX / gamma; + var rSurfaceY = kY / gamma; + var rSurfaceZ = kZ / gamma; + + if (defined(maxHeight)) { + position.x = rSurfaceX + nX * maxHeight; // top + position.y = rSurfaceY + nY * maxHeight; + position.z = rSurfaceZ + nZ * maxHeight; + } + + if (defined(minHeight)) { + extrudedPosition.x = rSurfaceX + nX * minHeight; // bottom + extrudedPosition.y = rSurfaceY + nY * minHeight; + extrudedPosition.z = rSurfaceZ + nZ * minHeight; + } + } + + function constructExtent(params) { + var extent = params.extent; + var ellipsoid = params.ellipsoid; + var size = params.size; + var height = params.height; + var width = params.width; + var surfaceHeight = params.surfaceHeight; + + var positions = new Float64Array(size * 3); + + var posIndex = 0; + var row = 0; + var col; + for (col = 0; col < width; col++) { + computePosition(params, row, col, surfaceHeight); + + positions[posIndex++] = position.x; + positions[posIndex++] = position.y; + positions[posIndex++] = position.z; + } + col = width - 1; + for (row = 1; row < height; row++) { + computePosition(params, row, col, surfaceHeight); + + positions[posIndex++] = position.x; + positions[posIndex++] = position.y; + positions[posIndex++] = position.z; + } + row = height - 1; + for (col = width-2; col >=0; col--){ + computePosition(params, row, col, surfaceHeight); + + positions[posIndex++] = position.x; + positions[posIndex++] = position.y; + positions[posIndex++] = position.z; + } + col = 0; + for (row = height - 2; row > 0; row--) { + computePosition(params, row, col, surfaceHeight); + + positions[posIndex++] = position.x; + positions[posIndex++] = position.y; + positions[posIndex++] = position.z; + } + var indicesSize = positions.length/3 * 2; + var indices = IndexDatatype.createTypedArray(positions.length / 3, indicesSize); + + var index = 0; + for(var i = 0; i < (positions.length/3)-1; i++) { + indices[index++] = i; + indices[index++] = i+1; + } + indices[index++] = (positions.length/3)-1; + indices[index++] = 0; + + return { + boundingSphere : BoundingSphere.fromExtent3D(extent, ellipsoid, surfaceHeight), + positions: positions, + indices: indices + }; + } + + function constructExtrudedExtent(params, extrudedHeight) { + var surfaceHeight = params.surfaceHeight; + var minHeight = Math.min(extrudedHeight, surfaceHeight); + var maxHeight = Math.max(extrudedHeight, surfaceHeight); + if (CesiumMath.equalsEpsilon(minHeight, maxHeight, 0.1)) { + return constructExtent(params); + } + var extent = params.extent; + var height = params.height; + var width = params.width; + var size = params.size * 3; + var ellipsoid = params.ellipsoid; + + var posIndex = 0; + var row = 0; + var col; + var positions = new Float64Array(size * 2); + for (col = 0; col < width; col++) { + computePosition(params, row, col, maxHeight, minHeight); + + positions[posIndex + size] = extrudedPosition.x; + positions[posIndex + size + 1] = extrudedPosition.y; + positions[posIndex + size + 2] = extrudedPosition.z; + + positions[posIndex++] = position.x; + positions[posIndex++] = position.y; + positions[posIndex++] = position.z; + } + col = width - 1; + for (row = 1; row < height; row++) { + computePosition(params, row, col, maxHeight, minHeight); + + positions[posIndex + size] = extrudedPosition.x; + positions[posIndex + size + 1] = extrudedPosition.y; + positions[posIndex + size + 2] = extrudedPosition.z; + + positions[posIndex++] = position.x; + positions[posIndex++] = position.y; + positions[posIndex++] = position.z; + } + row = height - 1; + for (col = width-2; col >=0; col--){ + computePosition(params, row, col, maxHeight, minHeight); + + positions[posIndex + size] = extrudedPosition.x; + positions[posIndex + size + 1] = extrudedPosition.y; + positions[posIndex + size + 2] = extrudedPosition.z; + + positions[posIndex++] = position.x; + positions[posIndex++] = position.y; + positions[posIndex++] = position.z; + } + col = 0; + for (row = height - 2; row > 0; row--) { + computePosition(params, row, col, maxHeight, minHeight); + + positions[posIndex + size] = extrudedPosition.x; + positions[posIndex + size + 1] = extrudedPosition.y; + positions[posIndex + size + 2] = extrudedPosition.z; + + positions[posIndex++] = position.x; + positions[posIndex++] = position.y; + positions[posIndex++] = position.z; + } + + var indicesSize = positions.length/3 * 2 + 8; + var indices = IndexDatatype.createTypedArray(positions.length / 3, indicesSize); + var length = positions.length/6; + var index = 0; + for (var i = 0; i < length - 1; i++) { + indices[index++] = i; + indices[index++] =i+1; + indices[index++] = i + length; + indices[index++] = i + length + 1; + } + indices[index++] = length - 1; + indices[index++] = 0; + indices[index++] = length + length - 1; + indices[index++] = length; + + indices[index++] = 0; + indices[index++] = length; + indices[index++] = width-1; + indices[index++] = length + width-1; + indices[index++] = width + height - 2; + indices[index++] = width + height - 2 + length; + indices[index++] = 2*width + height - 3; + indices[index++] = 2*width + height - 3 + length; + + + var topBS = BoundingSphere.fromExtent3D(extent, ellipsoid, maxHeight, topBoundingSphere); + var bottomBS = BoundingSphere.fromExtent3D(extent, ellipsoid, minHeight, bottomBoundingSphere); + var boundingSphere = BoundingSphere.union(topBS, bottomBS); + + return { + boundingSphere : boundingSphere, + positions: positions, + indices: indices + }; + } + + /** + * A description of the outline of a a cartographic extent on an ellipsoid centered at the origin. + * + * @alias ExtentOutlineGeometry + * @constructor + * + * @param {Extent} options.extent A cartographic extent with north, south, east and west properties in radians. + * @param {Ellipsoid} [options.ellipsoid=Ellipsoid.WGS84] The ellipsoid on which the extent lies. + * @param {Number} [options.granularity=CesiumMath.RADIANS_PER_DEGREE] The distance, in radians, between each latitude and longitude. Determines the number of positions in the buffer. + * @param {Number} [options.height=0.0] The height from the surface of the ellipsoid. + * @param {Number} [options.rotation=0.0] The rotation of the extent, in radians. A positive rotation is counter-clockwise. + * @param {Number} [options.extrudedHeight] Height of extruded surface. + * + * @exception {DeveloperError} <code>options.extent</code> is required and must have north, south, east and west attributes. + * @exception {DeveloperError} <code>options.extent.north</code> must be in the interval [<code>-Pi/2</code>, <code>Pi/2</code>]. + * @exception {DeveloperError} <code>options.extent.south</code> must be in the interval [<code>-Pi/2</code>, <code>Pi/2</code>]. + * @exception {DeveloperError} <code>options.extent.east</code> must be in the interval [<code>-Pi</code>, <code>Pi</code>]. + * @exception {DeveloperError} <code>options.extent.west</code> must be in the interval [<code>-Pi</code>, <code>Pi</code>]. + * @exception {DeveloperError} <code>options.extent.north</code> must be greater than <code>extent.south</code>. + * @exception {DeveloperError} <code>options.extent.east</code> must be greater than <code>extent.west</code>. + * + * @see ExtentOutlineGeometry#createGeometry + * + * @example + * var extent = new ExtentOutlineGeometry({ + * ellipsoid : Ellipsoid.WGS84, + * extent : Extent.fromDegrees(-80.0, 39.0, -74.0, 42.0), + * height : 10000.0 + * }); + * var geometry = ExtentOutlineGeometry.createGeometry(extent); + */ + var ExtentOutlineGeometry = function(options) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + + var extent = options.extent; + var granularity = defaultValue(options.granularity, CesiumMath.RADIANS_PER_DEGREE); + var ellipsoid = defaultValue(options.ellipsoid, Ellipsoid.WGS84); + var surfaceHeight = defaultValue(options.height, 0.0); + var rotation = options.rotation; + + if (!defined(extent)) { + throw new DeveloperError('extent is required.'); + } + + extent.validate(); + + this._extent = extent; + this._granularity = granularity; + this._ellipsoid = ellipsoid; + this._surfaceHeight = surfaceHeight; + this._rotation = rotation; + this._extrudedHeight = options.extrudedHeight; + this._workerName = 'createExtentOutlineGeometry'; + }; + + /** + * Computes the geometric representation of an outline of an extent, including its vertices, indices, and a bounding sphere. + * @memberof ExtentOutlineGeometry + * + * @param {ExtentOutlineGeometry} extentGeometry A description of the extent outline. + * @returns {Geometry} The computed vertices and indices. + * + * @exception {DeveloperError} Rotated extent is invalid. + */ + ExtentOutlineGeometry.createGeometry = function(extentGeometry) { + var extent = extentGeometry._extent; + var granularity = extentGeometry._granularity; + var ellipsoid = extentGeometry._ellipsoid; + var surfaceHeight = extentGeometry._surfaceHeight; + var rotation = extentGeometry._rotation; + var extrudedHeight = extentGeometry._extrudedHeight; + + var width = Math.ceil((extent.east - extent.west) / granularity) + 1; + var height = Math.ceil((extent.north - extent.south) / granularity) + 1; + var granularityX = (extent.east - extent.west) / (width - 1); + var granularityY = (extent.north - extent.south) / (height - 1); + + var radiiSquared = ellipsoid.getRadiiSquared(); + + extent.getNorthwest(nwCartographic); + extent.getCenter(centerCartographic); + + var granYCos = granularityY; + var granXCos = granularityX; + var granYSin = 0.0; + var granXSin = 0.0; + + if (defined(rotation)) { + var cosRotation = cos(rotation); + granYCos *= cosRotation; + granXCos *= cosRotation; + + var sinRotation = sin(rotation); + granYSin = granularityY * sinRotation; + granXSin = granularityX * sinRotation; + + proj.project(nwCartographic, nw); + proj.project(centerCartographic, center); + + Cartesian3.subtract(nw, center, nw); + Matrix2.fromRotation(rotation, rotationMatrix); + Matrix2.multiplyByVector(rotationMatrix, nw, nw); + Cartesian3.add(nw, center, nw); + proj.unproject(nw, nwCartographic); + + var latitude = nwCartographic.latitude; + var latitude0 = latitude + (width - 1) * granXSin; + var latitude1 = latitude - granYCos * (height - 1); + var latitude2 = latitude - granYCos * (height - 1) + (width - 1) * granXSin; + + var north = Math.max(latitude, latitude0, latitude1, latitude2); + var south = Math.min(latitude, latitude0, latitude1, latitude2); + + var longitude = nwCartographic.longitude; + var longitude0 = longitude + (width - 1) * granXCos; + var longitude1 = longitude + (height - 1) * granYSin; + var longitude2 = longitude + (height - 1) * granYSin + (width - 1) * granXCos; + + var east = Math.max(longitude, longitude0, longitude1, longitude2); + var west = Math.min(longitude, longitude0, longitude1, longitude2); + + if (!isValidLatLon(north, west) || !isValidLatLon(north, east) || + !isValidLatLon(south, west) || !isValidLatLon(south, east)) { + throw new DeveloperError('Rotated extent is invalid.'); + } + } + + var size = 2*width + 2*height - 4; + + var params = { + granYCos : granYCos, + granYSin : granYSin, + granXCos : granXCos, + granXSin : granXSin, + radiiSquared : radiiSquared, + ellipsoid : ellipsoid, + extent : extent, + width : width, + height : height, + surfaceHeight : surfaceHeight, + size : size + }; + + var geometry; + if (defined(extrudedHeight)) { + geometry = constructExtrudedExtent(params, extrudedHeight); + } else { + geometry = constructExtent(params); + } + + var attributes = new GeometryAttributes({ + position: new GeometryAttribute({ + componentDatatype : ComponentDatatype.DOUBLE, + componentsPerAttribute : 3, + values : geometry.positions + }) + }); + + return new Geometry({ + attributes : attributes, + indices : geometry.indices, + primitiveType : PrimitiveType.LINES, + boundingSphere : geometry.boundingSphere + }); + }; + + return ExtentOutlineGeometry; +}); + +/*global define*/ +define('Core/Extent',[ + './freezeObject', + './defaultValue', + './defined', + './Ellipsoid', + './Cartographic', + './DeveloperError', + './Math' + ], function( + freezeObject, + defaultValue, + defined, + Ellipsoid, + Cartographic, + DeveloperError, + CesiumMath) { + "use strict"; + + /** + * A two dimensional region specified as longitude and latitude coordinates. + * + * @alias Extent + * @constructor + * + * @param {Number} [west=0.0] The westernmost longitude, in radians, in the range [-Pi, Pi]. + * @param {Number} [south=0.0] The southernmost latitude, in radians, in the range [-Pi/2, Pi/2]. + * @param {Number} [east=0.0] The easternmost longitude, in radians, in the range [-Pi, Pi]. + * @param {Number} [north=0.0] The northernmost latitude, in radians, in the range [-Pi/2, Pi/2]. + */ + var Extent = function(west, south, east, north) { + /** + * The westernmost longitude in radians in the range [-Pi, Pi]. + * + * @type {Number} + * @default 0.0 + */ + this.west = defaultValue(west, 0.0); + + /** + * The southernmost latitude in radians in the range [-Pi/2, Pi/2]. + * + * @type {Number} + * @default 0.0 + */ + this.south = defaultValue(south, 0.0); + + /** + * The easternmost longitude in radians in the range [-Pi, Pi]. + * + * @type {Number} + * @default 0.0 + */ + this.east = defaultValue(east, 0.0); + + /** + * The northernmost latitude in radians in the range [-Pi/2, Pi/2]. + * + * @type {Number} + * @default 0.0 + */ + this.north = defaultValue(north, 0.0); + }; + + /** + * Creates an extent given the boundary longitude and latitude in degrees. + * + * @memberof Extent + * + * @param {Number} [west=0.0] The westernmost longitude in degrees in the range [-180.0, 180.0]. + * @param {Number} [south=0.0] The southernmost latitude in degrees in the range [-90.0, 90.0]. + * @param {Number} [east=0.0] The easternmost longitude in degrees in the range [-180.0, 180.0]. + * @param {Number} [north=0.0] The northernmost latitude in degrees in the range [-90.0, 90.0]. + * @param {Extent} [result] The object onto which to store the result, or undefined if a new instance should be created. + * + * @returns {Extent} The modified result parameter or a new Extent instance if none was provided. + * + * @example + * var extent = Extent.fromDegrees(0.0, 20.0, 10.0, 30.0); + */ + Extent.fromDegrees = function(west, south, east, north, result) { + west = CesiumMath.toRadians(defaultValue(west, 0.0)); + south = CesiumMath.toRadians(defaultValue(south, 0.0)); + east = CesiumMath.toRadians(defaultValue(east, 0.0)); + north = CesiumMath.toRadians(defaultValue(north, 0.0)); + + if (!defined(result)) { + return new Extent(west, south, east, north); + } + + result.west = west; + result.south = south; + result.east = east; + result.north = north; + + return result; + }; + + /** + * Creates the smallest possible Extent that encloses all positions in the provided array. + * @memberof Extent + * + * @param {Array} cartographics The list of Cartographic instances. + * @param {Extent} [result] The object onto which to store the result, or undefined if a new instance should be created. + * @returns {Extent} The modified result parameter or a new Extent instance if none was provided. + */ + Extent.fromCartographicArray = function(cartographics, result) { + if (!defined(cartographics)) { + throw new DeveloperError('cartographics is required.'); + } + + var minLon = Number.MAX_VALUE; + var maxLon = -Number.MAX_VALUE; + var minLat = Number.MAX_VALUE; + var maxLat = -Number.MAX_VALUE; + + for ( var i = 0, len = cartographics.length; i < len; i++) { + var position = cartographics[i]; + minLon = Math.min(minLon, position.longitude); + maxLon = Math.max(maxLon, position.longitude); + minLat = Math.min(minLat, position.latitude); + maxLat = Math.max(maxLat, position.latitude); + } + + if (!defined(result)) { + return new Extent(minLon, minLat, maxLon, maxLat); + } + + result.west = minLon; + result.south = minLat; + result.east = maxLon; + result.north = maxLat; + return result; + }; + + /** + * Duplicates an Extent. + * + * @memberof Extent + * + * @param {Extent} extent The extent to clone. + * @param {Extent} [result] The object onto which to store the result, or undefined if a new instance should be created. + * @returns {Extent} The modified result parameter or a new Extent instance if none was provided. (Returns undefined if extent is undefined) + */ + Extent.clone = function(extent, result) { + if (!defined(extent)) { + return undefined; + } + + if (!defined(result)) { + return new Extent(extent.west, extent.south, extent.east, extent.north); + } + + result.west = extent.west; + result.south = extent.south; + result.east = extent.east; + result.north = extent.north; + return result; + }; + + /** + * Duplicates this Extent. + * + * @memberof Extent + * + * @param {Extent} [result] The object onto which to store the result. + * @returns {Extent} The modified result parameter or a new Extent instance if none was provided. + */ + Extent.prototype.clone = function(result) { + return Extent.clone(this, result); + }; + + /** + * Compares the provided Extent with this Extent componentwise and returns + * <code>true</code> if they are equal, <code>false</code> otherwise. + * @memberof Extent + * + * @param {Extent} [other] The Extent to compare. + * @returns {Boolean} <code>true</code> if the Extents are equal, <code>false</code> otherwise. + */ + Extent.prototype.equals = function(other) { + return Extent.equals(this, other); + }; + + /** + * Compares the provided extents and returns <code>true</code> if they are equal, + * <code>false</code> otherwise. + * + * @memberof Extent + * + * @param {Extent} [left] The first Extent. + * @param {Extent} [right] The second Extent. + * + * @returns {Boolean} <code>true</code> if left and right are equal; otherwise <code>false</code>. + */ + Extent.equals = function(left, right) { + return (left === right) || + ((defined(left)) && + (defined(right)) && + (left.west === right.west) && + (left.south === right.south) && + (left.east === right.east) && + (left.north === right.north)); + }; + + /** + * Compares the provided Extent with this Extent componentwise and returns + * <code>true</code> if they are within the provided epsilon, + * <code>false</code> otherwise. + * @memberof Extent + * + * @param {Extent} [other] The Extent to compare. + * @param {Number} epsilon The epsilon to use for equality testing. + * @returns {Boolean} <code>true</code> if the Extents are within the provided epsilon, <code>false</code> otherwise. + * + * @exception {DeveloperError} epsilon is required and must be a number. + */ + Extent.prototype.equalsEpsilon = function(other, epsilon) { + if (typeof epsilon !== 'number') { + throw new DeveloperError('epsilon is required and must be a number.'); + } + + return defined(other) && + (Math.abs(this.west - other.west) <= epsilon) && + (Math.abs(this.south - other.south) <= epsilon) && + (Math.abs(this.east - other.east) <= epsilon) && + (Math.abs(this.north - other.north) <= epsilon); + }; + + /** + * Checks this Extent's properties and throws if they are not in valid ranges. + * + * @exception {DeveloperError} <code>north</code> is required to be a number. + * @exception {DeveloperError} <code>south</code> is required to be a number. + * @exception {DeveloperError} <code>east</code> is required to be a number. + * @exception {DeveloperError} <code>west</code> is required to be a number. + * @exception {DeveloperError} <code>north</code> must be in the interval [<code>-Pi/2</code>, <code>Pi/2</code>]. + * @exception {DeveloperError} <code>south</code> must be in the interval [<code>-Pi/2</code>, <code>Pi/2</code>]. + * @exception {DeveloperError} <code>east</code> must be in the interval [<code>-Pi</code>, <code>Pi</code>]. + * @exception {DeveloperError} <code>west</code> must be in the interval [<code>-Pi</code>, <code>Pi</code>]. + */ + Extent.prototype.validate = function() { + var north = this.north; + if (typeof north !== 'number') { + throw new DeveloperError('north is required to be a number.'); + } + + if (north < -CesiumMath.PI_OVER_TWO || north > CesiumMath.PI_OVER_TWO) { + throw new DeveloperError('north must be in the interval [-Pi/2, Pi/2].'); + } + + var south = this.south; + if (typeof south !== 'number') { + throw new DeveloperError('south is required to be a number.'); + } + + if (south < -CesiumMath.PI_OVER_TWO || south > CesiumMath.PI_OVER_TWO) { + throw new DeveloperError('south must be in the interval [-Pi/2, Pi/2].'); + } + + var west = this.west; + if (typeof west !== 'number') { + throw new DeveloperError('west is required to be a number.'); + } + + if (west < -Math.PI || west > Math.PI) { + throw new DeveloperError('west must be in the interval [-Pi, Pi].'); + } + + var east = this.east; + if (typeof east !== 'number') { + throw new DeveloperError('east is required to be a number.'); + } + + if (east < -Math.PI || east > Math.PI) { + throw new DeveloperError('east must be in the interval [-Pi, Pi].'); + } + }; + + /** + * Computes the southwest corner of this extent. + * @memberof Extent + * + * @param {Cartographic} [result] The object onto which to store the result. + * @returns {Cartographic} The modified result parameter or a new Cartographic instance if none was provided. + */ + Extent.prototype.getSouthwest = function(result) { + if (!defined(result)) { + return new Cartographic(this.west, this.south); + } + result.longitude = this.west; + result.latitude = this.south; + result.height = 0.0; + return result; + }; + + /** + * Computes the northwest corner of this extent. + * @memberof Extent + * + * @param {Cartographic} [result] The object onto which to store the result. + * @returns {Cartographic} The modified result parameter or a new Cartographic instance if none was provided. + */ + Extent.prototype.getNorthwest = function(result) { + if (!defined(result)) { + return new Cartographic(this.west, this.north); + } + result.longitude = this.west; + result.latitude = this.north; + result.height = 0.0; + return result; + }; + + /** + * Computes the northeast corner of this extent. + * @memberof Extent + * + * @param {Cartographic} [result] The object onto which to store the result. + * @returns {Cartographic} The modified result parameter or a new Cartographic instance if none was provided. + */ + Extent.prototype.getNortheast = function(result) { + if (!defined(result)) { + return new Cartographic(this.east, this.north); + } + result.longitude = this.east; + result.latitude = this.north; + result.height = 0.0; + return result; + }; + + /** + * Computes the southeast corner of this extent. + * @memberof Extent + * + * @param {Cartographic} [result] The object onto which to store the result. + * @returns {Cartographic} The modified result parameter or a new Cartographic instance if none was provided. + */ + Extent.prototype.getSoutheast = function(result) { + if (!defined(result)) { + return new Cartographic(this.east, this.south); + } + result.longitude = this.east; + result.latitude = this.south; + result.height = 0.0; + return result; + }; + + /** + * Computes the center of this extent. + * @memberof Extent + * + * @param {Cartographic} [result] The object onto which to store the result. + * @returns {Cartographic} The modified result parameter or a new Cartographic instance if none was provided. + */ + Extent.prototype.getCenter = function(result) { + if (!defined(result)) { + return new Cartographic((this.west + this.east) * 0.5, (this.south + this.north) * 0.5); + } + result.longitude = (this.west + this.east) * 0.5; + result.latitude = (this.south + this.north) * 0.5; + result.height = 0.0; + return result; + }; + + /** + * Computes the intersection of this extent with the provided extent. + * @memberof Extent + * + * @param otherExtent The extent to intersect with this extent. + * @param {Extent} [result] The object onto which to store the result. + * @returns {Extent} The modified result parameter or a new Extent instance if none was provided. + * + * @exception {DeveloperError} otherExtent is required. + */ + Extent.prototype.intersectWith = function(otherExtent, result) { + if (!defined(otherExtent)) { + throw new DeveloperError('otherExtent is required.'); + } + var west = Math.max(this.west, otherExtent.west); + var south = Math.max(this.south, otherExtent.south); + var east = Math.min(this.east, otherExtent.east); + var north = Math.min(this.north, otherExtent.north); + if (!defined(result)) { + return new Extent(west, south, east, north); + } + result.west = west; + result.south = south; + result.east = east; + result.north = north; + return result; + }; + + /** + * Returns true if the provided cartographic is on or inside the extent, false otherwise. + * @memberof Extent + * + * @param {Cartographic} cartographic The cartographic to test. + * @returns {Boolean} true if the provided cartographic is inside the extent, false otherwise. + * + * @exception {DeveloperError} cartographic is required. + */ + Extent.prototype.contains = function(cartographic) { + if (!defined(cartographic)) { + throw new DeveloperError('cartographic is required.'); + } + return cartographic.longitude >= this.west && + cartographic.longitude <= this.east && + cartographic.latitude >= this.south && + cartographic.latitude <= this.north; + }; + + /** + * Determines if the extent is empty, i.e., if <code>west >= east</code> + * or <code>south >= north</code>. + * + * @memberof Extent + * + * @returns {Boolean} True if the extent is empty; otherwise, false. + */ + Extent.prototype.isEmpty = function() { + return this.west >= this.east || this.south >= this.north; + }; + + var subsampleLlaScratch = new Cartographic(); + /** + * Samples this extent so that it includes a list of Cartesian points suitable for passing to + * {@link BoundingSphere#fromPoints}. Sampling is necessary to account + * for extents that cover the poles or cross the equator. + * + * @param {Ellipsoid} [ellipsoid=Ellipsoid.WGS84] The ellipsoid to use. + * @param {Number} [surfaceHeight=0.0] The height of the extent above the ellipsoid. + * @param {Array} [result] The array of Cartesians onto which to store the result. + * @returns {Array} The modified result parameter or a new Array of Cartesians instances if none was provided. + */ + Extent.prototype.subsample = function(ellipsoid, surfaceHeight, result) { + ellipsoid = defaultValue(ellipsoid, Ellipsoid.WGS84); + surfaceHeight = defaultValue(surfaceHeight, 0.0); + + if (!defined(result)) { + result = []; + } + var length = 0; + + var north = this.north; + var south = this.south; + var east = this.east; + var west = this.west; + + var lla = subsampleLlaScratch; + lla.height = surfaceHeight; + + lla.longitude = west; + lla.latitude = north; + result[length] = ellipsoid.cartographicToCartesian(lla, result[length]); + length++; + + lla.longitude = east; + result[length] = ellipsoid.cartographicToCartesian(lla, result[length]); + length++; + + lla.latitude = south; + result[length] = ellipsoid.cartographicToCartesian(lla, result[length]); + length++; + + lla.longitude = west; + result[length] = ellipsoid.cartographicToCartesian(lla, result[length]); + length++; + + if (north < 0.0) { + lla.latitude = north; + } else if (south > 0.0) { + lla.latitude = south; + } else { + lla.latitude = 0.0; + } + + for ( var i = 1; i < 8; ++i) { + var temp = -Math.PI + i * CesiumMath.PI_OVER_TWO; + if (west < temp && temp < east) { + lla.longitude = temp; + result[length] = ellipsoid.cartographicToCartesian(lla, result[length]); + length++; + } + } + + if (lla.latitude === 0.0) { + lla.longitude = west; + result[length] = ellipsoid.cartographicToCartesian(lla, result[length]); + length++; + lla.longitude = east; + result[length] = ellipsoid.cartographicToCartesian(lla, result[length]); + length++; + } + result.length = length; + return result; + }; + + /** + * The largest possible extent. + * @memberof Extent + * @type Extent + */ + Extent.MAX_VALUE = freezeObject(new Extent(-Math.PI, -CesiumMath.PI_OVER_TWO, Math.PI, CesiumMath.PI_OVER_TWO)); + + return Extent; +}); + +/*global define*/ +define('Core/Color',[ + './defaultValue', + './defined', + './freezeObject', + './DeveloperError', + './FeatureDetection', + './Math' + ], function( + defaultValue, + defined, + freezeObject, + DeveloperError, + FeatureDetection, + CesiumMath) { + "use strict"; + + function hue2rgb(m1, m2, h) { + if (h < 0) { + h += 1; + } + if (h > 1) { + h -= 1; + } + if (h * 6 < 1) { + return m1 + (m2 - m1) * 6 * h; + } + if (h * 2 < 1) { + return m2; + } + if (h * 3 < 2) { + return m1 + (m2 - m1) * (2 / 3 - h) * 6; + } + return m1; + } + + /** + * A color, specified using red, green, blue, and alpha values, + * which range from <code>0</code> (no intensity) to <code>1.0</code> (full intensity). + * @param {Number} [red=1.0] The red component. + * @param {Number} [green=1.0] The green component. + * @param {Number} [blue=1.0] The blue component. + * @param {Number} [alpha=1.0] The alpha component. + * + * @constructor + * @alias Color + * + * @see Packable + */ + var Color = function(red, green, blue, alpha) { + /** + * The red component. + * @type {Number} + * @default 1.0 + */ + this.red = defaultValue(red, 1.0); + /** + * The green component. + * @type {Number} + * @default 1.0 + */ + this.green = defaultValue(green, 1.0); + /** + * The blue component. + * @type {Number} + * @default 1.0 + */ + this.blue = defaultValue(blue, 1.0); + /** + * The alpha component. + * @type {Number} + * @default 1.0 + */ + this.alpha = defaultValue(alpha, 1.0); + }; + + /** + * Creates a new Color specified using red, green, blue, and alpha values + * that are in the range of 0 to 255, converting them internally to a range of 0.0 to 1.0. + * @memberof Color + * + * @param {Number} [red=255] The red component. + * @param {Number} [green=255] The green component. + * @param {Number} [blue=255] The blue component. + * @param {Number} [alpha=255] The alpha component. + * @returns {Color} A new color instance. + */ + Color.fromBytes = function(red, green, blue, alpha) { + red = Color.byteToFloat(defaultValue(red, 255.0)); + green = Color.byteToFloat(defaultValue(green, 255.0)); + blue = Color.byteToFloat(defaultValue(blue, 255.0)); + alpha = Color.byteToFloat(defaultValue(alpha, 255.0)); + return new Color(red, green, blue, alpha); + }; + + var scratchArrayBuffer; + var scratchUint32Array; + var scratchUint8Array; + if (FeatureDetection.supportsTypedArrays()) { + scratchArrayBuffer = new ArrayBuffer(4); + scratchUint32Array = new Uint32Array(scratchArrayBuffer); + scratchUint8Array = new Uint8Array(scratchArrayBuffer); + } + + /** + * Creates a new Color from a single numeric unsigned 32-bit RGBA value, using the endianness + * of the system. + * + * @memberof Color + * + * @param {Number} rgba A single numeric unsigned 32-bit RGBA value. + * @returns {Color} A new color instance. + * + * @example + * var color = Color.fromRgba(0x67ADDFFF); + * + * @see Color#toRgba + */ + Color.fromRgba = function(rgba) { + // scratchUint32Array and scratchUint8Array share an underlying array buffer + scratchUint32Array[0] = rgba; + return Color.fromBytes(scratchUint8Array[0], scratchUint8Array[1], scratchUint8Array[2], scratchUint8Array[3]); + }; + + /** + * Creates a Color instance from hue, saturation, and lightness. + * @memberof Color + * + * @param {Number} [hue=0] The hue angle 0...1 + * @param {Number} [saturation=0] The saturation value 0...1 + * @param {Number} [lightness=0] The lightness value 0...1 + * @param {Number} [alpha=1.0] The alpha component 0...1 + * @returns {Color} The color object. + * + * @see <a href="http://www.w3.org/TR/css3-color/#hsl-color">CSS color values</a> + */ + Color.fromHsl = function(hue, saturation, lightness, alpha) { + hue = defaultValue(hue, 0.0) % 1.0; + saturation = defaultValue(saturation, 0.0); + lightness = defaultValue(lightness, 0.0); + alpha = defaultValue(alpha, 1.0); + + var red = lightness; + var green = lightness; + var blue = lightness; + + if (saturation !== 0) { + var m2; + if (lightness < 0.5) { + m2 = lightness * (1 + saturation); + } else { + m2 = lightness + saturation - lightness * saturation; + } + + var m1 = 2.0 * lightness - m2; + red = hue2rgb(m1, m2, hue + 1 / 3); + green = hue2rgb(m1, m2, hue); + blue = hue2rgb(m1, m2, hue - 1 / 3); + } + + return new Color(red, green, blue, alpha); + }; + + /** + * Creates a random color using the provided options. For reproducible random colors, you should + * call {@link CesiumMath#setRandomNumberSeed} once at the beginning of your application. + * @memberof Color + * + * @param {Object} [options] Object containing the options. + * @param {Number} [options.red] If specified, the red component to use instead of a randomized value. + * @param {Number} [options.minimumRed=0.0] The maximum red value to generate if none was specified. + * @param {Number} [options.maximumRed=1.0] The minimum red value to generate if none was specified. + * @param {Number} [options.green] If specified, the green component to use instead of a randomized value. + * @param {Number} [options.minimumGreen=0.0] The maximum green value to generate if none was specified. + * @param {Number} [options.maximumGreen=1.0] The minimum green value to generate if none was specified. + * @param {Number} [options.blue] If specified, the blue component to use instead of a randomized value. + * @param {Number} [options.minimumBlue=0.0] The maximum blue value to generate if none was specified. + * @param {Number} [options.maximumBlue=1.0] The minimum blue value to generate if none was specified. + * @param {Number} [options.alpha] If specified, the alpha component to use instead of a randomized value. + * @param {Number} [options.minimumAlpha=0.0] The maximum alpha value to generate if none was specified. + * @param {Number} [options.maximumAlpha=1.0] The minimum alpha value to generate if none was specified. + * @param {Color} [result] The object to store the result in, if undefined a new instance will be created. + * + * @returns {Color} The modified result parameter or a new instance if result was undefined. + * + * @exception {DeveloperError} minimumRed must be less than or equal to maximumRed. + * @exception {DeveloperError} minimumGreen must be less than or equal to maximumGreen. + * @exception {DeveloperError} minimumBlue must be less than or equal to maximumBlue. + * @exception {DeveloperError} minimumAlpha must be less than or equal to maximumAlpha. + * + * @example + * //Create a completely random color + * var color = Color.fromRandom(); + * + * //Create a random shade of yellow. + * var color = Color.fromRandom({ + * red : 1.0, + * green : 1.0, + * alpha : 1.0 + * }); + * + * //Create a random bright color. + * var color = Color.fromRandom({ + * minimumRed : 0.75, + * minimumGreen : 0.75, + * minimumBlue : 0.75, + * alpha : 1.0 + * }); + */ + Color.fromRandom = function(options, result) { + options = defaultValue(options, defaultValue.EMPTY_OBJECT); + + var red = options.red; + if (!defined(red)) { + var minimumRed = defaultValue(options.minimumRed, 0); + var maximumRed = defaultValue(options.maximumRed, 1.0); + + if (minimumRed > maximumRed) { + throw new DeveloperError("minimumRed must be less than or equal to maximumRed"); + } + red = minimumRed + (CesiumMath.nextRandomNumber() * (maximumRed - minimumRed)); + } + + var green = options.green; + if (!defined(green)) { + var minimumGreen = defaultValue(options.minimumGreen, 0); + var maximumGreen = defaultValue(options.maximumGreen, 1.0); + + if (minimumGreen > maximumGreen) { + throw new DeveloperError("minimumGreen must be less than or equal to maximumGreen"); + } + green = minimumGreen + (CesiumMath.nextRandomNumber() * (maximumGreen - minimumGreen)); + } + + var blue = options.blue; + if (!defined(blue)) { + var minimumBlue = defaultValue(options.minimumBlue, 0); + var maximumBlue = defaultValue(options.maximumBlue, 1.0); + + if (minimumBlue > maximumBlue) { + throw new DeveloperError("minimumBlue must be less than or equal to maximumBlue"); + } + blue = minimumBlue + (CesiumMath.nextRandomNumber() * (maximumBlue - minimumBlue)); + } + + var alpha = options.alpha; + if (!defined(alpha)) { + var minimumAlpha = defaultValue(options.minimumAlpha, 0); + var maximumAlpha = defaultValue(options.maximumAlpha, 1.0); + + if (minimumAlpha > maximumAlpha) { + throw new DeveloperError("minimumAlpha must be less than or equal to maximumAlpha"); + } + alpha = minimumAlpha + (CesiumMath.nextRandomNumber() * (maximumAlpha - minimumAlpha)); + } + + if (!defined(result)) { + return new Color(red, green, blue, alpha); + } + + result.red = red; + result.green = green; + result.blue = blue; + result.alpha = alpha; + return result; + }; + + //#rgb + var rgbMatcher = /^#([0-9a-f])([0-9a-f])([0-9a-f])$/i; + //#rrggbb + var rrggbbMatcher = /^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i; + //rgb(), rgba(), or rgb%() + var rgbParenthesesMatcher = /^rgba?\(\s*([0-9.]+%?)\s*,\s*([0-9.]+%?)\s*,\s*([0-9.]+%?)(?:\s*,\s*([0-9.]+))?\s*\)$/i; + //hsl(), hsla(), or hsl%() + var hslParenthesesMatcher = /^hsla?\(\s*([0-9.]+)\s*,\s*([0-9.]+%)\s*,\s*([0-9.]+%)(?:\s*,\s*([0-9.]+))?\s*\)$/i; + + /** + * Creates a Color instance from a CSS color value. + * @memberof Color + * + * @param {String} color The CSS color value in #rgb, #rrggbb, rgb(), rgba(), hsl(), or hsla() format. + * @returns {Color} The color object, or undefined if the string was not a valid CSS color. + * + * @exception {DeveloperError} color is required. + * + * @example + * var cesiumBlue = Color.fromCssColorString('#67ADDF'); + * var green = Color.fromCssColorString('green'); + * + * @see <a href="http://www.w3.org/TR/css3-color">CSS color values</a> + */ + Color.fromCssColorString = function(color) { + if (!defined(color)) { + throw new DeveloperError('color is required'); + } + + var namedColor = Color[color.toUpperCase()]; + if (defined(namedColor)) { + return Color.clone(namedColor); + } + + var matches = rgbMatcher.exec(color); + if (matches !== null) { + return new Color(parseInt(matches[1], 16) / 15.0, + parseInt(matches[2], 16) / 15.0, + parseInt(matches[3], 16) / 15.0); + } + + matches = rrggbbMatcher.exec(color); + if (matches !== null) { + return new Color(parseInt(matches[1], 16) / 255.0, + parseInt(matches[2], 16) / 255.0, + parseInt(matches[3], 16) / 255.0); + } + + matches = rgbParenthesesMatcher.exec(color); + if (matches !== null) { + return new Color(parseFloat(matches[1]) / ('%' === matches[1].substr(-1) ? 100.0 : 255.0), + parseFloat(matches[2]) / ('%' === matches[2].substr(-1) ? 100.0 : 255.0), + parseFloat(matches[3]) / ('%' === matches[3].substr(-1) ? 100.0 : 255.0), + parseFloat(defaultValue(matches[4], '1.0'))); + } + + matches = hslParenthesesMatcher.exec(color); + if (matches !== null) { + return Color.fromHsl(parseFloat(matches[1]) / 360.0, + parseFloat(matches[2]) / 100.0, + parseFloat(matches[3]) / 100.0, + parseFloat(defaultValue(matches[4], '1.0'))); + } + + return undefined; + }; + + /** + * The number of elements used to pack the object into an array. + * @Type {Number} + */ + Color.packedLength = 4; + + /** + * Stores the provided instance into the provided array. + * @memberof Color + * + * @param {Color} value The value to pack. + * @param {Array} array The array to pack into. + * @param {Number} [startingIndex=0] The index into the array at which to start packing the elements. + * + * @exception {DeveloperError} value is required. + * @exception {DeveloperError} array is required. + */ + Color.pack = function(value, array, startingIndex) { + if (!defined(value)) { + throw new DeveloperError('value is required'); + } + + if (!defined(array)) { + throw new DeveloperError('array is required'); + } + + startingIndex = defaultValue(startingIndex, 0); + + array[startingIndex++] = value.red; + array[startingIndex++] = value.green; + array[startingIndex++] = value.blue; + array[startingIndex] = value.alpha; + }; + + /** + * Retrieves an instance from a packed array. + * @memberof Color + * + * @param {Array} array The packed array. + * @param {Number} [startingIndex=0] The starting index of the element to be unpacked. + * @param {Color} [result] The object into which to store the result. + * + * @exception {DeveloperError} array is required. + */ + Color.unpack = function(array, startingIndex, result) { + if (!defined(array)) { + throw new DeveloperError('array is required'); + } + + startingIndex = defaultValue(startingIndex, 0); + + if (!defined(result)) { + result = new Color(); + } + result.red = array[startingIndex++]; + result.green = array[startingIndex++]; + result.blue = array[startingIndex++]; + result.alpha = array[startingIndex]; + return result; + }; + + /** + * Converts a 'byte' color component in the range of 0 to 255 into + * a 'float' color component in the range of 0 to 1.0. + * @memberof Color + * + * @param {Number} number The number to be converted. + * @returns {number} The converted number. + */ + Color.byteToFloat = function(number) { + return number / 255.0; + }; + + /** + * Converts a 'float' color component in the range of 0 to 1.0 into + * a 'byte' color component in the range of 0 to 255. + * @memberof Color + * + * @param {Number} number The number to be converted. + * @returns {number} The converted number. + */ + Color.floatToByte = function(number) { + return number === 1.0 ? 255.0 : (number * 256.0) | 0; + }; + + /** + * Duplicates a Color. + * @memberof Color + * + * @param {Color} color The Color to duplicate. + * @param {Color} [result] The object to store the result in, if undefined a new instance will be created. + * @returns {Color} The modified result parameter or a new instance if result was undefined. (Returns undefined if color is undefined) + */ + Color.clone = function(color, result) { + if (!defined(color)) { + return undefined; + } + if (!defined(result)) { + return new Color(color.red, color.green, color.blue, color.alpha); + } + result.red = color.red; + result.green = color.green; + result.blue = color.blue; + result.alpha = color.alpha; + return result; + }; + + /** + * Returns true if the first Color equals the second color. + * @memberof Color + * + * @param {Color} left The first Color to compare for equality. + * @param {Color} right The second Color to compare for equality. + * @returns {Boolean} <code>true</code> if the Colors are equal; otherwise, <code>false</code>. + */ + Color.equals = function(left, right) { + return (left === right) || // + (defined(left) && // + defined(right) && // + left.red === right.red && // + left.green === right.green && // + left.blue === right.blue && // + left.alpha === right.alpha); + }; + + /** + * Returns a duplicate of a Color instance. + * @memberof Color + * + * @param {Color} [result] The object to store the result in, if undefined a new instance will be created. + * @returns {Color} The modified result parameter or a new instance if result was undefined. + */ + Color.prototype.clone = function(result) { + return Color.clone(this, result); + }; + + /** + * Returns true if this Color equals other. + * @memberof Color + * + * @param {Color} other The Color to compare for equality. + * @returns {Boolean} <code>true</code> if the Colors are equal; otherwise, <code>false</code>. + */ + Color.prototype.equals = function(other) { + return Color.equals(this, other); + }; + + /** + * Returns <code>true</code> if this Color equals other componentwise within the specified epsilon. + * @memberof Color + * + * @param {Color} other The Color to compare for equality. + * @param {Number} [epsilon=0.0] The epsilon to use for equality testing. + * @returns {Boolean} <code>true</code> if the Colors are equal within the specified epsilon; otherwise, <code>false</code>. + */ + Color.prototype.equalsEpsilon = function(other, epsilon) { + return (this === other) || // + ((defined(other)) && // + (Math.abs(this.red - other.red) <= epsilon) && // + (Math.abs(this.green - other.green) <= epsilon) && // + (Math.abs(this.blue - other.blue) <= epsilon) && // + (Math.abs(this.alpha - other.alpha) <= epsilon)); + }; + + /** + * Creates a string representing this Color in the format '(red, green, blue, alpha)'. + * @memberof Color + * + * @returns {String} A string representing this Color in the format '(red, green, blue, alpha)'. + */ + Color.prototype.toString = function() { + return '(' + this.red + ', ' + this.green + ', ' + this.blue + ', ' + this.alpha + ')'; + }; + + /** + * Creates a string containing the CSS color value for this color. + * @memberof Color + * + * @returns {String} The CSS equivalent of this color. + * @see <a href="http://www.w3.org/TR/css3-color/#rgba-color">CSS RGB or RGBA color values</a> + */ + Color.prototype.toCssColorString = function() { + var red = Color.floatToByte(this.red); + var green = Color.floatToByte(this.green); + var blue = Color.floatToByte(this.blue); + if (this.alpha === 1) { + return 'rgb(' + red + ',' + green + ',' + blue + ')'; + } + return 'rgba(' + red + ',' + green + ',' + blue + ',' + this.alpha + ')'; + }; + + /** + * Converts this color to an array of red, green, blue, and alpha values + * that are in the range of 0 to 255. + * @memberof Color + * + * @param {Array} [result] The array to store the result in, if undefined a new instance will be created. + * @returns {Array} The modified result parameter or a new instance if result was undefined. + */ + Color.prototype.toBytes = function(result) { + var red = Color.floatToByte(this.red); + var green = Color.floatToByte(this.green); + var blue = Color.floatToByte(this.blue); + var alpha = Color.floatToByte(this.alpha); + + if (!defined(result)) { + return [red, green, blue, alpha]; + } + result[0] = red; + result[1] = green; + result[2] = blue; + result[3] = alpha; + return result; + }; + + /** + * Converts this color to a single numeric unsigned 32-bit RGBA value, using the endianness + * of the system. + * + * @memberof Color + * + * @returns {Number} A single numeric unsigned 32-bit RGBA value. + * + * @example + * var rgba = Color.BLUE.toRgba(); + * + * @see Color.fromRgba + */ + Color.prototype.toRgba = function() { + // scratchUint32Array and scratchUint8Array share an underlying array buffer + scratchUint8Array[0] = Color.floatToByte(this.red); + scratchUint8Array[1] = Color.floatToByte(this.green); + scratchUint8Array[2] = Color.floatToByte(this.blue); + scratchUint8Array[3] = Color.floatToByte(this.alpha); + return scratchUint32Array[0]; + }; + + /** + * An immutable Color instance initialized to CSS color #F0F8FF + * <span class="colorSwath" style="background: #F0F8FF;"></span> + * + * @constant + * @type {Color} + */ + Color.ALICEBLUE = freezeObject(Color.fromCssColorString('#F0F8FF')); + + /** + * An immutable Color instance initialized to CSS color #FAEBD7 + * <span class="colorSwath" style="background: #FAEBD7;"></span> + * + * @constant + * @type {Color} + */ + Color.ANTIQUEWHITE = freezeObject(Color.fromCssColorString('#FAEBD7')); + + /** + * An immutable Color instance initialized to CSS color #00FFFF + * <span class="colorSwath" style="background: #00FFFF;"></span> + * + * @constant + * @type {Color} + */ + Color.AQUA = freezeObject(Color.fromCssColorString('#00FFFF')); + + /** + * An immutable Color instance initialized to CSS color #7FFFD4 + * <span class="colorSwath" style="background: #7FFFD4;"></span> + * + * @constant + * @type {Color} + */ + Color.AQUAMARINE = freezeObject(Color.fromCssColorString('#7FFFD4')); + + /** + * An immutable Color instance initialized to CSS color #F0FFFF + * <span class="colorSwath" style="background: #F0FFFF;"></span> + * + * @constant + * @type {Color} + */ + Color.AZURE = freezeObject(Color.fromCssColorString('#F0FFFF')); + + /** + * An immutable Color instance initialized to CSS color #F5F5DC + * <span class="colorSwath" style="background: #F5F5DC;"></span> + * + * @constant + * @type {Color} + */ + Color.BEIGE = freezeObject(Color.fromCssColorString('#F5F5DC')); + + /** + * An immutable Color instance initialized to CSS color #FFE4C4 + * <span class="colorSwath" style="background: #FFE4C4;"></span> + * + * @constant + * @type {Color} + */ + Color.BISQUE = freezeObject(Color.fromCssColorString('#FFE4C4')); + + /** + * An immutable Color instance initialized to CSS color #000000 + * <span class="colorSwath" style="background: #000000;"></span> + * + * @constant + * @type {Color} + */ + Color.BLACK = freezeObject(Color.fromCssColorString('#000000')); + + /** + * An immutable Color instance initialized to CSS color #FFEBCD + * <span class="colorSwath" style="background: #FFEBCD;"></span> + * + * @constant + * @type {Color} + */ + Color.BLANCHEDALMOND = freezeObject(Color.fromCssColorString('#FFEBCD')); + + /** + * An immutable Color instance initialized to CSS color #0000FF + * <span class="colorSwath" style="background: #0000FF;"></span> + * + * @constant + * @type {Color} + */ + Color.BLUE = freezeObject(Color.fromCssColorString('#0000FF')); + + /** + * An immutable Color instance initialized to CSS color #8A2BE2 + * <span class="colorSwath" style="background: #8A2BE2;"></span> + * + * @constant + * @type {Color} + */ + Color.BLUEVIOLET = freezeObject(Color.fromCssColorString('#8A2BE2')); + + /** + * An immutable Color instance initialized to CSS color #A52A2A + * <span class="colorSwath" style="background: #A52A2A;"></span> + * + * @constant + * @type {Color} + */ + Color.BROWN = freezeObject(Color.fromCssColorString('#A52A2A')); + + /** + * An immutable Color instance initialized to CSS color #DEB887 + * <span class="colorSwath" style="background: #DEB887;"></span> + * + * @constant + * @type {Color} + */ + Color.BURLYWOOD = freezeObject(Color.fromCssColorString('#DEB887')); + + /** + * An immutable Color instance initialized to CSS color #5F9EA0 + * <span class="colorSwath" style="background: #5F9EA0;"></span> + * + * @constant + * @type {Color} + */ + Color.CADETBLUE = freezeObject(Color.fromCssColorString('#5F9EA0')); + /** + * An immutable Color instance initialized to CSS color #7FFF00 + * <span class="colorSwath" style="background: #7FFF00;"></span> + * + * @constant + * @type {Color} + */ + Color.CHARTREUSE = freezeObject(Color.fromCssColorString('#7FFF00')); + + /** + * An immutable Color instance initialized to CSS color #D2691E + * <span class="colorSwath" style="background: #D2691E;"></span> + * + * @constant + * @type {Color} + */ + Color.CHOCOLATE = freezeObject(Color.fromCssColorString('#D2691E')); + + /** + * An immutable Color instance initialized to CSS color #FF7F50 + * <span class="colorSwath" style="background: #FF7F50;"></span> + * + * @constant + * @type {Color} + */ + Color.CORAL = freezeObject(Color.fromCssColorString('#FF7F50')); + + /** + * An immutable Color instance initialized to CSS color #6495ED + * <span class="colorSwath" style="background: #6495ED;"></span> + * + * @constant + * @type {Color} + */ + Color.CORNFLOWERBLUE = freezeObject(Color.fromCssColorString('#6495ED')); + + /** + * An immutable Color instance initialized to CSS color #FFF8DC + * <span class="colorSwath" style="background: #FFF8DC;"></span> + * + * @constant + * @type {Color} + */ + Color.CORNSILK = freezeObject(Color.fromCssColorString('#FFF8DC')); + + /** + * An immutable Color instance initialized to CSS color #DC143C + * <span class="colorSwath" style="background: #DC143C;"></span> + * + * @constant + * @type {Color} + */ + Color.CRIMSON = freezeObject(Color.fromCssColorString('#DC143C')); + + /** + * An immutable Color instance initialized to CSS color #00FFFF + * <span class="colorSwath" style="background: #00FFFF;"></span> + * + * @constant + * @type {Color} + */ + Color.CYAN = freezeObject(Color.fromCssColorString('#00FFFF')); + + /** + * An immutable Color instance initialized to CSS color #00008B + * <span class="colorSwath" style="background: #00008B;"></span> + * + * @constant + * @type {Color} + */ + Color.DARKBLUE = freezeObject(Color.fromCssColorString('#00008B')); + + /** + * An immutable Color instance initialized to CSS color #008B8B + * <span class="colorSwath" style="background: #008B8B;"></span> + * + * @constant + * @type {Color} + */ + Color.DARKCYAN = freezeObject(Color.fromCssColorString('#008B8B')); + + /** + * An immutable Color instance initialized to CSS color #B8860B + * <span class="colorSwath" style="background: #B8860B;"></span> + * + * @constant + * @type {Color} + */ + Color.DARKGOLDENROD = freezeObject(Color.fromCssColorString('#B8860B')); + + /** + * An immutable Color instance initialized to CSS color #A9A9A9 + * <span class="colorSwath" style="background: #A9A9A9;"></span> + * + * @constant + * @type {Color} + */ + Color.DARKGRAY = freezeObject(Color.fromCssColorString('#A9A9A9')); + + /** + * An immutable Color instance initialized to CSS color #006400 + * <span class="colorSwath" style="background: #006400;"></span> + * + * @constant + * @type {Color} + */ + Color.DARKGREEN = freezeObject(Color.fromCssColorString('#006400')); + + /** + * An immutable Color instance initialized to CSS color #A9A9A9 + * <span class="colorSwath" style="background: #A9A9A9;"></span> + * + * @constant + * @type {Color} + */ + Color.DARKGREY = Color.DARKGRAY; + + /** + * An immutable Color instance initialized to CSS color #BDB76B + * <span class="colorSwath" style="background: #BDB76B;"></span> + * + * @constant + * @type {Color} + */ + Color.DARKKHAKI = freezeObject(Color.fromCssColorString('#BDB76B')); + + /** + * An immutable Color instance initialized to CSS color #8B008B + * <span class="colorSwath" style="background: #8B008B;"></span> + * + * @constant + * @type {Color} + */ + Color.DARKMAGENTA = freezeObject(Color.fromCssColorString('#8B008B')); + + /** + * An immutable Color instance initialized to CSS color #556B2F + * <span class="colorSwath" style="background: #556B2F;"></span> + * + * @constant + * @type {Color} + */ + Color.DARKOLIVEGREEN = freezeObject(Color.fromCssColorString('#556B2F')); + + /** + * An immutable Color instance initialized to CSS color #FF8C00 + * <span class="colorSwath" style="background: #FF8C00;"></span> + * + * @constant + * @type {Color} + */ + Color.DARKORANGE = freezeObject(Color.fromCssColorString('#FF8C00')); + + /** + * An immutable Color instance initialized to CSS color #9932CC + * <span class="colorSwath" style="background: #9932CC;"></span> + * + * @constant + * @type {Color} + */ + Color.DARKORCHID = freezeObject(Color.fromCssColorString('#9932CC')); + + /** + * An immutable Color instance initialized to CSS color #8B0000 + * <span class="colorSwath" style="background: #8B0000;"></span> + * + * @constant + * @type {Color} + */ + Color.DARKRED = freezeObject(Color.fromCssColorString('#8B0000')); + + /** + * An immutable Color instance initialized to CSS color #E9967A + * <span class="colorSwath" style="background: #E9967A;"></span> + * + * @constant + * @type {Color} + */ + Color.DARKSALMON = freezeObject(Color.fromCssColorString('#E9967A')); + + /** + * An immutable Color instance initialized to CSS color #8FBC8F + * <span class="colorSwath" style="background: #8FBC8F;"></span> + * + * @constant + * @type {Color} + */ + Color.DARKSEAGREEN = freezeObject(Color.fromCssColorString('#8FBC8F')); + + /** + * An immutable Color instance initialized to CSS color #483D8B + * <span class="colorSwath" style="background: #483D8B;"></span> + * + * @constant + * @type {Color} + */ + Color.DARKSLATEBLUE = freezeObject(Color.fromCssColorString('#483D8B')); + + /** + * An immutable Color instance initialized to CSS color #2F4F4F + * <span class="colorSwath" style="background: #2F4F4F;"></span> + * + * @constant + * @type {Color} + */ + Color.DARKSLATEGRAY = freezeObject(Color.fromCssColorString('#2F4F4F')); + + /** + * An immutable Color instance initialized to CSS color #2F4F4F + * <span class="colorSwath" style="background: #2F4F4F;"></span> + * + * @constant + * @type {Color} + */ + Color.DARKSLATEGREY = Color.DARKSLATEGRAY; + + /** + * An immutable Color instance initialized to CSS color #00CED1 + * <span class="colorSwath" style="background: #00CED1;"></span> + * + * @constant + * @type {Color} + */ + Color.DARKTURQUOISE = freezeObject(Color.fromCssColorString('#00CED1')); + + /** + * An immutable Color instance initialized to CSS color #9400D3 + * <span class="colorSwath" style="background: #9400D3;"></span> + * + * @constant + * @type {Color} + */ + Color.DARKVIOLET = freezeObject(Color.fromCssColorString('#9400D3')); + + /** + * An immutable Color instance initialized to CSS color #FF1493 + * <span class="colorSwath" style="background: #FF1493;"></span> + * + * @constant + * @type {Color} + */ + Color.DEEPPINK = freezeObject(Color.fromCssColorString('#FF1493')); + + /** + * An immutable Color instance initialized to CSS color #00BFFF + * <span class="colorSwath" style="background: #00BFFF;"></span> + * + * @constant + * @type {Color} + */ + Color.DEEPSKYBLUE = freezeObject(Color.fromCssColorString('#00BFFF')); + + /** + * An immutable Color instance initialized to CSS color #696969 + * <span class="colorSwath" style="background: #696969;"></span> + * + * @constant + * @type {Color} + */ + Color.DIMGRAY = freezeObject(Color.fromCssColorString('#696969')); + + /** + * An immutable Color instance initialized to CSS color #696969 + * <span class="colorSwath" style="background: #696969;"></span> + * + * @constant + * @type {Color} + */ + Color.DIMGREY = Color.DIMGRAY; + + /** + * An immutable Color instance initialized to CSS color #1E90FF + * <span class="colorSwath" style="background: #1E90FF;"></span> + * + * @constant + * @type {Color} + */ + Color.DODGERBLUE = freezeObject(Color.fromCssColorString('#1E90FF')); + + /** + * An immutable Color instance initialized to CSS color #B22222 + * <span class="colorSwath" style="background: #B22222;"></span> + * + * @constant + * @type {Color} + */ + Color.FIREBRICK = freezeObject(Color.fromCssColorString('#B22222')); + + /** + * An immutable Color instance initialized to CSS color #FFFAF0 + * <span class="colorSwath" style="background: #FFFAF0;"></span> + * + * @constant + * @type {Color} + */ + Color.FLORALWHITE = freezeObject(Color.fromCssColorString('#FFFAF0')); + + /** + * An immutable Color instance initialized to CSS color #228B22 + * <span class="colorSwath" style="background: #228B22;"></span> + * + * @constant + * @type {Color} + */ + Color.FORESTGREEN = freezeObject(Color.fromCssColorString('#228B22')); + + /** + * An immutable Color instance initialized to CSS color #FF00FF + * <span class="colorSwath" style="background: #FF00FF;"></span> + * + * @constant + * @type {Color} + */ + Color.FUSCHIA = freezeObject(Color.fromCssColorString('#FF00FF')); + + /** + * An immutable Color instance initialized to CSS color #DCDCDC + * <span class="colorSwath" style="background: #DCDCDC;"></span> + * + * @constant + * @type {Color} + */ + Color.GAINSBORO = freezeObject(Color.fromCssColorString('#DCDCDC')); + + /** + * An immutable Color instance initialized to CSS color #F8F8FF + * <span class="colorSwath" style="background: #F8F8FF;"></span> + * + * @constant + * @type {Color} + */ + Color.GHOSTWHITE = freezeObject(Color.fromCssColorString('#F8F8FF')); + + /** + * An immutable Color instance initialized to CSS color #FFD700 + * <span class="colorSwath" style="background: #FFD700;"></span> + * + * @constant + * @type {Color} + */ + Color.GOLD = freezeObject(Color.fromCssColorString('#FFD700')); + + /** + * An immutable Color instance initialized to CSS color #DAA520 + * <span class="colorSwath" style="background: #DAA520;"></span> + * + * @constant + * @type {Color} + */ + Color.GOLDENROD = freezeObject(Color.fromCssColorString('#DAA520')); + + /** + * An immutable Color instance initialized to CSS color #808080 + * <span class="colorSwath" style="background: #808080;"></span> + * + * @constant + * @type {Color} + */ + Color.GRAY = freezeObject(Color.fromCssColorString('#808080')); + + /** + * An immutable Color instance initialized to CSS color #008000 + * <span class="colorSwath" style="background: #008000;"></span> + * + * @constant + * @type {Color} + */ + Color.GREEN = freezeObject(Color.fromCssColorString('#008000')); + + /** + * An immutable Color instance initialized to CSS color #ADFF2F + * <span class="colorSwath" style="background: #ADFF2F;"></span> + * + * @constant + * @type {Color} + */ + Color.GREENYELLOW = freezeObject(Color.fromCssColorString('#ADFF2F')); + + /** + * An immutable Color instance initialized to CSS color #808080 + * <span class="colorSwath" style="background: #808080;"></span> + * + * @constant + * @type {Color} + */ + Color.GREY = Color.GRAY; + + /** + * An immutable Color instance initialized to CSS color #F0FFF0 + * <span class="colorSwath" style="background: #F0FFF0;"></span> + * + * @constant + * @type {Color} + */ + Color.HONEYDEW = freezeObject(Color.fromCssColorString('#F0FFF0')); + + /** + * An immutable Color instance initialized to CSS color #FF69B4 + * <span class="colorSwath" style="background: #FF69B4;"></span> + * + * @constant + * @type {Color} + */ + Color.HOTPINK = freezeObject(Color.fromCssColorString('#FF69B4')); + + /** + * An immutable Color instance initialized to CSS color #CD5C5C + * <span class="colorSwath" style="background: #CD5C5C;"></span> + * + * @constant + * @type {Color} + */ + Color.INDIANRED = freezeObject(Color.fromCssColorString('#CD5C5C')); + + /** + * An immutable Color instance initialized to CSS color #4B0082 + * <span class="colorSwath" style="background: #4B0082;"></span> + * + * @constant + * @type {Color} + */ + Color.INDIGO = freezeObject(Color.fromCssColorString('#4B0082')); + + /** + * An immutable Color instance initialized to CSS color #FFFFF0 + * <span class="colorSwath" style="background: #FFFFF0;"></span> + * + * @constant + * @type {Color} + */ + Color.IVORY = freezeObject(Color.fromCssColorString('#FFFFF0')); + + /** + * An immutable Color instance initialized to CSS color #F0E68C + * <span class="colorSwath" style="background: #F0E68C;"></span> + * + * @constant + * @type {Color} + */ + Color.KHAKI = freezeObject(Color.fromCssColorString('#F0E68C')); + + /** + * An immutable Color instance initialized to CSS color #E6E6FA + * <span class="colorSwath" style="background: #E6E6FA;"></span> + * + * @constant + * @type {Color} + */ + Color.LAVENDER = freezeObject(Color.fromCssColorString('#E6E6FA')); + + /** + * An immutable Color instance initialized to CSS color #FFF0F5 + * <span class="colorSwath" style="background: #FFF0F5;"></span> + * + * @constant + * @type {Color} + */ + Color.LAVENDAR_BLUSH = freezeObject(Color.fromCssColorString('#FFF0F5')); + + /** + * An immutable Color instance initialized to CSS color #7CFC00 + * <span class="colorSwath" style="background: #7CFC00;"></span> + * + * @constant + * @type {Color} + */ + Color.LAWNGREEN = freezeObject(Color.fromCssColorString('#7CFC00')); + + /** + * An immutable Color instance initialized to CSS color #FFFACD + * <span class="colorSwath" style="background: #FFFACD;"></span> + * + * @constant + * @type {Color} + */ + Color.LEMONCHIFFON = freezeObject(Color.fromCssColorString('#FFFACD')); + + /** + * An immutable Color instance initialized to CSS color #ADD8E6 + * <span class="colorSwath" style="background: #ADD8E6;"></span> + * + * @constant + * @type {Color} + */ + Color.LIGHTBLUE = freezeObject(Color.fromCssColorString('#ADD8E6')); + + /** + * An immutable Color instance initialized to CSS color #F08080 + * <span class="colorSwath" style="background: #F08080;"></span> + * + * @constant + * @type {Color} + */ + Color.LIGHTCORAL = freezeObject(Color.fromCssColorString('#F08080')); + + /** + * An immutable Color instance initialized to CSS color #E0FFFF + * <span class="colorSwath" style="background: #E0FFFF;"></span> + * + * @constant + * @type {Color} + */ + Color.LIGHTCYAN = freezeObject(Color.fromCssColorString('#E0FFFF')); + + /** + * An immutable Color instance initialized to CSS color #FAFAD2 + * <span class="colorSwath" style="background: #FAFAD2;"></span> + * + * @constant + * @type {Color} + */ + Color.LIGHTGOLDENRODYELLOW = freezeObject(Color.fromCssColorString('#FAFAD2')); + + /** + * An immutable Color instance initialized to CSS color #D3D3D3 + * <span class="colorSwath" style="background: #D3D3D3;"></span> + * + * @constant + * @type {Color} + */ + Color.LIGHTGRAY = freezeObject(Color.fromCssColorString('#D3D3D3')); + + /** + * An immutable Color instance initialized to CSS color #90EE90 + * <span class="colorSwath" style="background: #90EE90;"></span> + * + * @constant + * @type {Color} + */ + Color.LIGHTGREEN = freezeObject(Color.fromCssColorString('#90EE90')); + + /** + * An immutable Color instance initialized to CSS color #D3D3D3 + * <span class="colorSwath" style="background: #D3D3D3;"></span> + * + * @constant + * @type {Color} + */ + Color.LIGHTGREY = Color.LIGHTGRAY; + + /** + * An immutable Color instance initialized to CSS color #FFB6C1 + * <span class="colorSwath" style="background: #FFB6C1;"></span> + * + * @constant + * @type {Color} + */ + Color.LIGHTPINK = freezeObject(Color.fromCssColorString('#FFB6C1')); + + /** + * An immutable Color instance initialized to CSS color #20B2AA + * <span class="colorSwath" style="background: #20B2AA;"></span> + * + * @constant + * @type {Color} + */ + Color.LIGHTSEAGREEN = freezeObject(Color.fromCssColorString('#20B2AA')); + + /** + * An immutable Color instance initialized to CSS color #87CEFA + * <span class="colorSwath" style="background: #87CEFA;"></span> + * + * @constant + * @type {Color} + */ + Color.LIGHTSKYBLUE = freezeObject(Color.fromCssColorString('#87CEFA')); + + /** + * An immutable Color instance initialized to CSS color #778899 + * <span class="colorSwath" style="background: #778899;"></span> + * + * @constant + * @type {Color} + */ + Color.LIGHTSLATEGRAY = freezeObject(Color.fromCssColorString('#778899')); + + /** + * An immutable Color instance initialized to CSS color #778899 + * <span class="colorSwath" style="background: #778899;"></span> + * + * @constant + * @type {Color} + */ + Color.LIGHTSLATEGREY = Color.LIGHTSLATEGRAY; + + /** + * An immutable Color instance initialized to CSS color #B0C4DE + * <span class="colorSwath" style="background: #B0C4DE;"></span> + * + * @constant + * @type {Color} + */ + Color.LIGHTSTEELBLUE = freezeObject(Color.fromCssColorString('#B0C4DE')); + + /** + * An immutable Color instance initialized to CSS color #FFFFE0 + * <span class="colorSwath" style="background: #FFFFE0;"></span> + * + * @constant + * @type {Color} + */ + Color.LIGHTYELLOW = freezeObject(Color.fromCssColorString('#FFFFE0')); + + /** + * An immutable Color instance initialized to CSS color #00FF00 + * <span class="colorSwath" style="background: #00FF00;"></span> + * + * @constant + * @type {Color} + */ + Color.LIME = freezeObject(Color.fromCssColorString('#00FF00')); + + /** + * An immutable Color instance initialized to CSS color #32CD32 + * <span class="colorSwath" style="background: #32CD32;"></span> + * + * @constant + * @type {Color} + */ + Color.LIMEGREEN = freezeObject(Color.fromCssColorString('#32CD32')); + + /** + * An immutable Color instance initialized to CSS color #FAF0E6 + * <span class="colorSwath" style="background: #FAF0E6;"></span> + * + * @constant + * @type {Color} + */ + Color.LINEN = freezeObject(Color.fromCssColorString('#FAF0E6')); + + /** + * An immutable Color instance initialized to CSS color #FF00FF + * <span class="colorSwath" style="background: #FF00FF;"></span> + * + * @constant + * @type {Color} + */ + Color.MAGENTA = freezeObject(Color.fromCssColorString('#FF00FF')); + + /** + * An immutable Color instance initialized to CSS color #800000 + * <span class="colorSwath" style="background: #800000;"></span> + * + * @constant + * @type {Color} + */ + Color.MAROON = freezeObject(Color.fromCssColorString('#800000')); + + /** + * An immutable Color instance initialized to CSS color #66CDAA + * <span class="colorSwath" style="background: #66CDAA;"></span> + * + * @constant + * @type {Color} + */ + Color.MEDIUMAQUAMARINE = freezeObject(Color.fromCssColorString('#66CDAA')); + + /** + * An immutable Color instance initialized to CSS color #0000CD + * <span class="colorSwath" style="background: #0000CD;"></span> + * + * @constant + * @type {Color} + */ + Color.MEDIUMBLUE = freezeObject(Color.fromCssColorString('#0000CD')); + + /** + * An immutable Color instance initialized to CSS color #BA55D3 + * <span class="colorSwath" style="background: #BA55D3;"></span> + * + * @constant + * @type {Color} + */ + Color.MEDIUMORCHID = freezeObject(Color.fromCssColorString('#BA55D3')); + + /** + * An immutable Color instance initialized to CSS color #9370DB + * <span class="colorSwath" style="background: #9370DB;"></span> + * + * @constant + * @type {Color} + */ + Color.MEDIUMPURPLE = freezeObject(Color.fromCssColorString('#9370DB')); + + /** + * An immutable Color instance initialized to CSS color #3CB371 + * <span class="colorSwath" style="background: #3CB371;"></span> + * + * @constant + * @type {Color} + */ + Color.MEDIUMSEAGREEN = freezeObject(Color.fromCssColorString('#3CB371')); + + /** + * An immutable Color instance initialized to CSS color #7B68EE + * <span class="colorSwath" style="background: #7B68EE;"></span> + * + * @constant + * @type {Color} + */ + Color.MEDIUMSLATEBLUE = freezeObject(Color.fromCssColorString('#7B68EE')); + + /** + * An immutable Color instance initialized to CSS color #00FA9A + * <span class="colorSwath" style="background: #00FA9A;"></span> + * + * @constant + * @type {Color} + */ + Color.MEDIUMSPRINGGREEN = freezeObject(Color.fromCssColorString('#00FA9A')); + + /** + * An immutable Color instance initialized to CSS color #48D1CC + * <span class="colorSwath" style="background: #48D1CC;"></span> + * + * @constant + * @type {Color} + */ + Color.MEDIUMTURQUOISE = freezeObject(Color.fromCssColorString('#48D1CC')); + + /** + * An immutable Color instance initialized to CSS color #C71585 + * <span class="colorSwath" style="background: #C71585;"></span> + * + * @constant + * @type {Color} + */ + Color.MEDIUMVIOLETRED = freezeObject(Color.fromCssColorString('#C71585')); + + /** + * An immutable Color instance initialized to CSS color #191970 + * <span class="colorSwath" style="background: #191970;"></span> + * + * @constant + * @type {Color} + */ + Color.MIDNIGHTBLUE = freezeObject(Color.fromCssColorString('#191970')); + + /** + * An immutable Color instance initialized to CSS color #F5FFFA + * <span class="colorSwath" style="background: #F5FFFA;"></span> + * + * @constant + * @type {Color} + */ + Color.MINTCREAM = freezeObject(Color.fromCssColorString('#F5FFFA')); + + /** + * An immutable Color instance initialized to CSS color #FFE4E1 + * <span class="colorSwath" style="background: #FFE4E1;"></span> + * + * @constant + * @type {Color} + */ + Color.MISTYROSE = freezeObject(Color.fromCssColorString('#FFE4E1')); + + /** + * An immutable Color instance initialized to CSS color #FFE4B5 + * <span class="colorSwath" style="background: #FFE4B5;"></span> + * + * @constant + * @type {Color} + */ + Color.MOCCASIN = freezeObject(Color.fromCssColorString('#FFE4B5')); + + /** + * An immutable Color instance initialized to CSS color #FFDEAD + * <span class="colorSwath" style="background: #FFDEAD;"></span> + * + * @constant + * @type {Color} + */ + Color.NAVAJOWHITE = freezeObject(Color.fromCssColorString('#FFDEAD')); + + /** + * An immutable Color instance initialized to CSS color #000080 + * <span class="colorSwath" style="background: #000080;"></span> + * + * @constant + * @type {Color} + */ + Color.NAVY = freezeObject(Color.fromCssColorString('#000080')); + + /** + * An immutable Color instance initialized to CSS color #FDF5E6 + * <span class="colorSwath" style="background: #FDF5E6;"></span> + * + * @constant + * @type {Color} + */ + Color.OLDLACE = freezeObject(Color.fromCssColorString('#FDF5E6')); + + /** + * An immutable Color instance initialized to CSS color #808000 + * <span class="colorSwath" style="background: #808000;"></span> + * + * @constant + * @type {Color} + */ + Color.OLIVE = freezeObject(Color.fromCssColorString('#808000')); + + /** + * An immutable Color instance initialized to CSS color #6B8E23 + * <span class="colorSwath" style="background: #6B8E23;"></span> + * + * @constant + * @type {Color} + */ + Color.OLIVEDRAB = freezeObject(Color.fromCssColorString('#6B8E23')); + + /** + * An immutable Color instance initialized to CSS color #FFA500 + * <span class="colorSwath" style="background: #FFA500;"></span> + * + * @constant + * @type {Color} + */ + Color.ORANGE = freezeObject(Color.fromCssColorString('#FFA500')); + + /** + * An immutable Color instance initialized to CSS color #FF4500 + * <span class="colorSwath" style="background: #FF4500;"></span> + * + * @constant + * @type {Color} + */ + Color.ORANGERED = freezeObject(Color.fromCssColorString('#FF4500')); + + /** + * An immutable Color instance initialized to CSS color #DA70D6 + * <span class="colorSwath" style="background: #DA70D6;"></span> + * + * @constant + * @type {Color} + */ + Color.ORCHID = freezeObject(Color.fromCssColorString('#DA70D6')); + + /** + * An immutable Color instance initialized to CSS color #EEE8AA + * <span class="colorSwath" style="background: #EEE8AA;"></span> + * + * @constant + * @type {Color} + */ + Color.PALEGOLDENROD = freezeObject(Color.fromCssColorString('#EEE8AA')); + + /** + * An immutable Color instance initialized to CSS color #98FB98 + * <span class="colorSwath" style="background: #98FB98;"></span> + * + * @constant + * @type {Color} + */ + Color.PALEGREEN = freezeObject(Color.fromCssColorString('#98FB98')); + + /** + * An immutable Color instance initialized to CSS color #AFEEEE + * <span class="colorSwath" style="background: #AFEEEE;"></span> + * + * @constant + * @type {Color} + */ + Color.PALETURQUOISE = freezeObject(Color.fromCssColorString('#AFEEEE')); + + /** + * An immutable Color instance initialized to CSS color #DB7093 + * <span class="colorSwath" style="background: #DB7093;"></span> + * + * @constant + * @type {Color} + */ + Color.PALEVIOLETRED = freezeObject(Color.fromCssColorString('#DB7093')); + + /** + * An immutable Color instance initialized to CSS color #FFEFD5 + * <span class="colorSwath" style="background: #FFEFD5;"></span> + * + * @constant + * @type {Color} + */ + Color.PAPAYAWHIP = freezeObject(Color.fromCssColorString('#FFEFD5')); + + /** + * An immutable Color instance initialized to CSS color #FFDAB9 + * <span class="colorSwath" style="background: #FFDAB9;"></span> + * + * @constant + * @type {Color} + */ + Color.PEACHPUFF = freezeObject(Color.fromCssColorString('#FFDAB9')); + + /** + * An immutable Color instance initialized to CSS color #CD853F + * <span class="colorSwath" style="background: #CD853F;"></span> + * + * @constant + * @type {Color} + */ + Color.PERU = freezeObject(Color.fromCssColorString('#CD853F')); + + /** + * An immutable Color instance initialized to CSS color #FFC0CB + * <span class="colorSwath" style="background: #FFC0CB;"></span> + * + * @constant + * @type {Color} + */ + Color.PINK = freezeObject(Color.fromCssColorString('#FFC0CB')); + + /** + * An immutable Color instance initialized to CSS color #DDA0DD + * <span class="colorSwath" style="background: #DDA0DD;"></span> + * + * @constant + * @type {Color} + */ + Color.PLUM = freezeObject(Color.fromCssColorString('#DDA0DD')); + + /** + * An immutable Color instance initialized to CSS color #B0E0E6 + * <span class="colorSwath" style="background: #B0E0E6;"></span> + * + * @constant + * @type {Color} + */ + Color.POWDERBLUE = freezeObject(Color.fromCssColorString('#B0E0E6')); + + /** + * An immutable Color instance initialized to CSS color #800080 + * <span class="colorSwath" style="background: #800080;"></span> + * + * @constant + * @type {Color} + */ + Color.PURPLE = freezeObject(Color.fromCssColorString('#800080')); + + /** + * An immutable Color instance initialized to CSS color #FF0000 + * <span class="colorSwath" style="background: #FF0000;"></span> + * + * @constant + * @type {Color} + */ + Color.RED = freezeObject(Color.fromCssColorString('#FF0000')); + + /** + * An immutable Color instance initialized to CSS color #BC8F8F + * <span class="colorSwath" style="background: #BC8F8F;"></span> + * + * @constant + * @type {Color} + */ + Color.ROSYBROWN = freezeObject(Color.fromCssColorString('#BC8F8F')); + + /** + * An immutable Color instance initialized to CSS color #4169E1 + * <span class="colorSwath" style="background: #4169E1;"></span> + * + * @constant + * @type {Color} + */ + Color.ROYALBLUE = freezeObject(Color.fromCssColorString('#4169E1')); + + /** + * An immutable Color instance initialized to CSS color #8B4513 + * <span class="colorSwath" style="background: #8B4513;"></span> + * + * @constant + * @type {Color} + */ + Color.SADDLEBROWN = freezeObject(Color.fromCssColorString('#8B4513')); + + /** + * An immutable Color instance initialized to CSS color #FA8072 + * <span class="colorSwath" style="background: #FA8072;"></span> + * + * @constant + * @type {Color} + */ + Color.SALMON = freezeObject(Color.fromCssColorString('#FA8072')); + + /** + * An immutable Color instance initialized to CSS color #F4A460 + * <span class="colorSwath" style="background: #F4A460;"></span> + * + * @constant + * @type {Color} + */ + Color.SANDYBROWN = freezeObject(Color.fromCssColorString('#F4A460')); + + /** + * An immutable Color instance initialized to CSS color #2E8B57 + * <span class="colorSwath" style="background: #2E8B57;"></span> + * + * @constant + * @type {Color} + */ + Color.SEAGREEN = freezeObject(Color.fromCssColorString('#2E8B57')); + + /** + * An immutable Color instance initialized to CSS color #FFF5EE + * <span class="colorSwath" style="background: #FFF5EE;"></span> + * + * @constant + * @type {Color} + */ + Color.SEASHELL = freezeObject(Color.fromCssColorString('#FFF5EE')); + + /** + * An immutable Color instance initialized to CSS color #A0522D + * <span class="colorSwath" style="background: #A0522D;"></span> + * + * @constant + * @type {Color} + */ + Color.SIENNA = freezeObject(Color.fromCssColorString('#A0522D')); + + /** + * An immutable Color instance initialized to CSS color #C0C0C0 + * <span class="colorSwath" style="background: #C0C0C0;"></span> + * + * @constant + * @type {Color} + */ + Color.SILVER = freezeObject(Color.fromCssColorString('#C0C0C0')); + + /** + * An immutable Color instance initialized to CSS color #87CEEB + * <span class="colorSwath" style="background: #87CEEB;"></span> + * + * @constant + * @type {Color} + */ + Color.SKYBLUE = freezeObject(Color.fromCssColorString('#87CEEB')); + + /** + * An immutable Color instance initialized to CSS color #6A5ACD + * <span class="colorSwath" style="background: #6A5ACD;"></span> + * + * @constant + * @type {Color} + */ + Color.SLATEBLUE = freezeObject(Color.fromCssColorString('#6A5ACD')); + + /** + * An immutable Color instance initialized to CSS color #708090 + * <span class="colorSwath" style="background: #708090;"></span> + * + * @constant + * @type {Color} + */ + Color.SLATEGRAY = freezeObject(Color.fromCssColorString('#708090')); + + /** + * An immutable Color instance initialized to CSS color #708090 + * <span class="colorSwath" style="background: #708090;"></span> + * + * @constant + * @type {Color} + */ + Color.SLATEGREY = Color.SLATEGRAY; + + /** + * An immutable Color instance initialized to CSS color #FFFAFA + * <span class="colorSwath" style="background: #FFFAFA;"></span> + * + * @constant + * @type {Color} + */ + Color.SNOW = freezeObject(Color.fromCssColorString('#FFFAFA')); + + /** + * An immutable Color instance initialized to CSS color #00FF7F + * <span class="colorSwath" style="background: #00FF7F;"></span> + * + * @constant + * @type {Color} + */ + Color.SPRINGGREEN = freezeObject(Color.fromCssColorString('#00FF7F')); + + /** + * An immutable Color instance initialized to CSS color #4682B4 + * <span class="colorSwath" style="background: #4682B4;"></span> + * + * @constant + * @type {Color} + */ + Color.STEELBLUE = freezeObject(Color.fromCssColorString('#4682B4')); + + /** + * An immutable Color instance initialized to CSS color #D2B48C + * <span class="colorSwath" style="background: #D2B48C;"></span> + * + * @constant + * @type {Color} + */ + Color.TAN = freezeObject(Color.fromCssColorString('#D2B48C')); + + /** + * An immutable Color instance initialized to CSS color #008080 + * <span class="colorSwath" style="background: #008080;"></span> + * + * @constant + * @type {Color} + */ + Color.TEAL = freezeObject(Color.fromCssColorString('#008080')); + + /** + * An immutable Color instance initialized to CSS color #D8BFD8 + * <span class="colorSwath" style="background: #D8BFD8;"></span> + * + * @constant + * @type {Color} + */ + Color.THISTLE = freezeObject(Color.fromCssColorString('#D8BFD8')); + + /** + * An immutable Color instance initialized to CSS color #FF6347 + * <span class="colorSwath" style="background: #FF6347;"></span> + * + * @constant + * @type {Color} + */ + Color.TOMATO = freezeObject(Color.fromCssColorString('#FF6347')); + + /** + * An immutable Color instance initialized to CSS color #40E0D0 + * <span class="colorSwath" style="background: #40E0D0;"></span> + * + * @constant + * @type {Color} + */ + Color.TURQUOISE = freezeObject(Color.fromCssColorString('#40E0D0')); + + /** + * An immutable Color instance initialized to CSS color #EE82EE + * <span class="colorSwath" style="background: #EE82EE;"></span> + * + * @constant + * @type {Color} + */ + Color.VIOLET = freezeObject(Color.fromCssColorString('#EE82EE')); + + /** + * An immutable Color instance initialized to CSS color #F5DEB3 + * <span class="colorSwath" style="background: #F5DEB3;"></span> + * + * @constant + * @type {Color} + */ + Color.WHEAT = freezeObject(Color.fromCssColorString('#F5DEB3')); + + /** + * An immutable Color instance initialized to CSS color #FFFFFF + * <span class="colorSwath" style="background: #FFFFFF;"></span> + * + * @constant + * @type {Color} + */ + Color.WHITE = freezeObject(Color.fromCssColorString('#FFFFFF')); + + /** + * An immutable Color instance initialized to CSS color #F5F5F5 + * <span class="colorSwath" style="background: #F5F5F5;"></span> + * + * @constant + * @type {Color} + */ + Color.WHITESMOKE = freezeObject(Color.fromCssColorString('#F5F5F5')); + + /** + * An immutable Color instance initialized to CSS color #FFFF00 + * <span class="colorSwath" style="background: #FFFF00;"></span> + * + * @constant + * @type {Color} + */ + Color.YELLOW = freezeObject(Color.fromCssColorString('#FFFF00')); + + /** + * An immutable Color instance initialized to CSS color #9ACD32 + * <span class="colorSwath" style="background: #9ACD32;"></span> + * + * @constant + * @type {Color} + */ + Color.YELLOWGREEN = freezeObject(Color.fromCssColorString('#9ACD32')); + + return Color; +}); + +/*global define*/ +define('Core/barycentricCoordinates',[ + './Cartesian2', + './Cartesian3', + './defined', + './DeveloperError' + ], function( + Cartesian2, + Cartesian3, + defined, + DeveloperError) { + "use strict"; + + var scratchCartesian1 = new Cartesian3(); + var scratchCartesian2 = new Cartesian3(); + var scratchCartesian3 = new Cartesian3(); + + /** + * Computes the barycentric coordinates for a point with respect to a triangle. + * + * @exports pointInsideTriangle + * + * @param {Cartesian2|Cartesian3} point The point to test. + * @param {Cartesian2|Cartesian3} p0 The first point of the triangle, corresponding to the barycentric x-axis. + * @param {Cartesian2|Cartesian3} p1 The second point of the triangle, corresponding to the barycentric y-axis. + * @param {Cartesian2|Cartesian3} p2 The third point of the triangle, corresponding to the barycentric z-axis. + * @param {Cartesian3} [result] The object onto which to store the result. + * + * @returns {Cartesian3} The modified result parameter or a new Cartesian3 instance if one was not provided. + * + * @exception {DeveloperError} point, p0, p1, and p2 are required. + * + * @example + * // Returns Cartesian3.UNIT_X + * var p = new Cartesian3(-1.0, 0.0, 0.0); + * var b = barycentricCoordinates(p, + * new Cartesian3(-1.0, 0.0, 0.0), + * new Cartesian3( 1.0, 0.0, 0.0), + * new Cartesian3( 0.0, 1.0, 1.0)); + */ + var barycentricCoordinates = function(point, p0, p1, p2, result) { + if (!defined(point) || !defined(p0) || !defined(p1) || !defined(p2)) { + throw new DeveloperError('point, p0, p1, and p2 are required.'); + } + + if (!defined(result)) { + result = new Cartesian3(); + } + + // Implementation based on http://www.blackpawn.com/texts/pointinpoly/default.html. + var v0, v1, v2; + var dot00, dot01, dot02, dot11, dot12; + + if(!defined(p0.z)) { + v0 = Cartesian2.subtract(p1, p0, scratchCartesian1); + v1 = Cartesian2.subtract(p2, p0, scratchCartesian2); + v2 = Cartesian2.subtract(point, p0, scratchCartesian3); + + dot00 = Cartesian2.dot(v0, v0); + dot01 = Cartesian2.dot(v0, v1); + dot02 = Cartesian2.dot(v0, v2); + dot11 = Cartesian2.dot(v1, v1); + dot12 = Cartesian2.dot(v1, v2); + } else { + v0 = Cartesian3.subtract(p1, p0, scratchCartesian1); + v1 = Cartesian3.subtract(p2, p0, scratchCartesian2); + v2 = Cartesian3.subtract(point, p0, scratchCartesian3); + + dot00 = Cartesian3.dot(v0, v0); + dot01 = Cartesian3.dot(v0, v1); + dot02 = Cartesian3.dot(v0, v2); + dot11 = Cartesian3.dot(v1, v1); + dot12 = Cartesian3.dot(v1, v2); + } + + var q = 1.0 / (dot00 * dot11 - dot01 * dot01); + result.y = (dot11 * dot02 - dot01 * dot12) * q; + result.z = (dot00 * dot12 - dot01 * dot02) * q; + result.x = 1.0 - result.y - result.z; + return result; + }; + + return barycentricCoordinates; +}); + +/*global define*/ +define('Core/EncodedCartesian3',[ + './Cartesian3', + './defined', + './DeveloperError' + ], function( + Cartesian3, + defined, + DeveloperError) { + "use strict"; + + /** + * A fixed-point encoding of a {@link Cartesian3} with 64-bit floating-point components, as two {@link Cartesian3} + * values that, when converted to 32-bit floating-point and added, approximate the original input. + * <p> + * This is used to encode positions in vertex buffers for rendering without jittering artifacts + * as described in <a href="http://blogs.agi.com/insight3d/index.php/2008/09/03/precisions-precisions/">Precisions, Precisions</a>. + * </p> + * + * @alias EncodedCartesian3 + * @constructor + * + * @see czm_modelViewRelativeToEye + * @see czm_modelViewProjectionRelativeToEye + */ + var EncodedCartesian3 = function() { + /** + * The high bits for each component. Bits 0 to 22 store the whole value. Bits 23 to 31 are not used. + * <p> + * The default is {@link Cartesian3.ZERO}. + * </p> + * + * @type {Cartesian3} + * @default {@link Cartesian3.ZERO} + */ + this.high = Cartesian3.clone(Cartesian3.ZERO); + + /** + * The low bits for each component. Bits 7 to 22 store the whole value, and bits 0 to 6 store the fraction. Bits 23 to 31 are not used. + * <p> + * The default is {@link Cartesian3.ZERO}. + * </p> + * + * @type {Cartesian3} + * @default {@link Cartesian3.ZERO} + */ + this.low = Cartesian3.clone(Cartesian3.ZERO); + }; + + /** + * Encodes a 64-bit floating-point value as two floating-point values that, when converted to + * 32-bit floating-point and added, approximate the original input. The returned object + * has <code>high</code> and <code>low</code> properties for the high and low bits, respectively. + * <p> + * The fixed-point encoding follows <a href="http://blogs.agi.com/insight3d/index.php/2008/09/03/precisions-precisions/">Precisions, Precisions</a>. + * </p> + * @memberof EncodedCartesian3 + * + * @param {Number} value The floating-point value to encode. + * @param {Object} [result] The object onto which to store the result. + * + * @returns {Object} The modified result parameter or a new instance if one was not provided. + * + * @exception {DeveloperError} value is required. + * + * @example + * var value = 1234567.1234567; + * var splitValue = EncodedCartesian3.encode(value); + */ + EncodedCartesian3.encode = function(value, result) { + if (!defined(value)) { + throw new DeveloperError('value is required'); + } + + if (!defined(result)) { + result = { + high : 0.0, + low : 0.0 + }; + } + + var doubleHigh; + if (value >= 0.0) { + doubleHigh = Math.floor(value / 65536.0) * 65536.0; + result.high = doubleHigh; + result.low = value - doubleHigh; + } else { + doubleHigh = Math.floor(-value / 65536.0) * 65536.0; + result.high = -doubleHigh; + result.low = value + doubleHigh; + } + + return result; + }; + + var scratchEncode = { + high : 0.0, + low : 0.0 + }; + + /** + * Encodes a {@link Cartesian3} with 64-bit floating-point components as two {@link Cartesian3} + * values that, when converted to 32-bit floating-point and added, approximate the original input. + * <p> + * The fixed-point encoding follows <a href="http://blogs.agi.com/insight3d/index.php/2008/09/03/precisions-precisions/">Precisions, Precisions</a>. + * </p> + * @memberof EncodedCartesian3 + * + * @param {Cartesian3} cartesian The cartesian to encode. + * @param {EncodedCartesian3} [result] The object onto which to store the result. + * @returns {EncodedCartesian3} The modified result parameter or a new EncodedCartesian3 instance if one was not provided. + * + * @exception {DeveloperError} cartesian is required. + * + * @example + * var cart = new Cartesian3(-10000000.0, 0.0, 10000000.0); + * var encoded = EncodedCartesian3.fromCartesian(cart); + */ + EncodedCartesian3.fromCartesian = function(cartesian, result) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + if (!defined(result)) { + result = new EncodedCartesian3(); + } + + var high = result.high; + var low = result.low; + + EncodedCartesian3.encode(cartesian.x, scratchEncode); + high.x = scratchEncode.high; + low.x = scratchEncode.low; + + EncodedCartesian3.encode(cartesian.y, scratchEncode); + high.y = scratchEncode.high; + low.y = scratchEncode.low; + + EncodedCartesian3.encode(cartesian.z, scratchEncode); + high.z = scratchEncode.high; + low.z = scratchEncode.low; + + return result; + }; + + var encodedP = new EncodedCartesian3(); + + /** + * Encodes the provided <code>cartesian</code>, and writes it to an array with <code>high</code> + * components followed by <code>low</code> components, i.e. <code>[high.x, high.y, high.z, low.x, low.y, low.z]</code>. + * <p> + * This is used to create interleaved high-precision position vertex attributes. + * </p> + * + * @param {Cartesian3} cartesian The cartesian to encode. + * @param {Array} cartesianArray The array to write to. + * @param {Number} index The index into the array to start writing. Six elements will be written. + * + * @exception {DeveloperError} cartesian is required. + * @exception {DeveloperError} cartesianArray is required. + * @exception {DeveloperError} index must be a number greater than or equal to 0. + * + * @example + * var positions = [ + * new Cartesian3(), + * // ... + * ]; + * var encodedPositions = new Float32Array(2 * 3 * positions.length); + * var j = 0; + * for (var i = 0; i < positions.length; ++i) { + * EncodedCartesian3.writeElement(positions[i], encodedPositions, j); + * j += 6; + * } + */ + EncodedCartesian3.writeElements = function(cartesian, cartesianArray, index) { + if (!defined(cartesian)) { + throw new DeveloperError('cartesian is required'); + } + + if (!defined(cartesianArray)) { + throw new DeveloperError('cartesianArray is required'); + } + + if (typeof index !== 'number' || index < 0) { + throw new DeveloperError('index must be a number greater than or equal to 0.'); + } + + EncodedCartesian3.fromCartesian(cartesian, encodedP); + var high = encodedP.high; + var low = encodedP.low; + + cartesianArray[index] = high.x; + cartesianArray[index + 1] = high.y; + cartesianArray[index + 2] = high.z; + cartesianArray[index + 3] = low.x; + cartesianArray[index + 4] = low.y; + cartesianArray[index + 5] = low.z; + }; + + return EncodedCartesian3; +}); + +/*global define*/ +define('Core/QuadraticRealPolynomial',[ + './DeveloperError', + './Math' + ], + function( + DeveloperError, + CesiumMath) { + "use strict"; + + /** + * Defines functions for 2nd order polynomial functions of one variable with only real coefficients. + * + * @exports QuadraticRealPolynomial + */ + var QuadraticRealPolynomial = {}; + + /** + * Provides the discriminant of the quadratic equation from the supplied coefficients. + * @memberof QuadraticRealPolynomial + * + * @param {Number} a The coefficient of the 2nd order monomial. + * @param {Number} b The coefficient of the 1st order monomial. + * @param {Number} c The coefficient of the 0th order monomial. + * @returns {Number} The value of the discriminant. + * + * @exception {DeveloperError} a is a required number. + * @exception {DeveloperError} b is a required number. + * @exception {DeveloperError} c is a required number. + */ + QuadraticRealPolynomial.discriminant = function(a, b, c) { + if (typeof a !== 'number') { + throw new DeveloperError('a is a required number.'); + } + if (typeof b !== 'number') { + throw new DeveloperError('b is a required number.'); + } + if (typeof c !== 'number') { + throw new DeveloperError('c is a required number.'); + } + + var discriminant = b * b - 4.0 * a * c; + return discriminant; + }; + + function addWithCancellationCheck(left, right, tolerance) { + var difference = left + right; + if ((CesiumMath.sign(left) !== CesiumMath.sign(right)) && + Math.abs(difference / Math.max(Math.abs(left), Math.abs(right))) < tolerance) { + return 0.0; + } + + return difference; + } + + /** + * Provides the real valued roots of the quadratic polynomial with the provided coefficients. + * @memberof QuadraticRealPolynomial + * + * @param {Number} a The coefficient of the 2nd order monomial. + * @param {Number} b The coefficient of the 1st order monomial. + * @param {Number} c The coefficient of the 0th order monomial. + * @returns {Array} The real valued roots. + * + * @exception {DeveloperError} a is a required number. + * @exception {DeveloperError} b is a required number. + * @exception {DeveloperError} c is a required number. + */ + QuadraticRealPolynomial.realRoots = function(a, b, c) { + if (typeof a !== 'number') { + throw new DeveloperError('a is a required number.'); + } + if (typeof b !== 'number') { + throw new DeveloperError('b is a required number.'); + } + if (typeof c !== 'number') { + throw new DeveloperError('c is a required number.'); + } + + var ratio; + if (a === 0.0) { + if (b === 0.0) { + // Constant function: c = 0. + return []; + } + + // Linear function: b * x + c = 0. + return [-c / b]; + } else if (b === 0.0) { + if (c === 0.0) { + // 2nd order monomial: a * x^2 = 0. + return [0.0, 0.0]; + } + + var cMagnitude = Math.abs(c); + var aMagnitude = Math.abs(a); + + if ((cMagnitude < aMagnitude) && (cMagnitude / aMagnitude < CesiumMath.EPSILON14)) { // c ~= 0.0. + // 2nd order monomial: a * x^2 = 0. + return [0.0, 0.0]; + } else if ((cMagnitude > aMagnitude) && (aMagnitude / cMagnitude < CesiumMath.EPSILON14)) { // a ~= 0.0. + // Constant function: c = 0. + return []; + } + + // a * x^2 + c = 0 + ratio = -c / a; + + if (ratio < 0.0) { + // Both roots are complex. + return []; + } + + // Both roots are real. + var root = Math.sqrt(ratio); + return [-root, root]; + } else if (c === 0.0) { + // a * x^2 + b * x = 0 + ratio = -b / a; + if (ratio < 0.0) { + return [ratio, 0.0]; + } + + return [0.0, ratio]; + } + + // a * x^2 + b * x + c = 0 + var b2 = b * b; + var four_ac = 4.0 * a * c; + var radicand = addWithCancellationCheck(b2, -four_ac, CesiumMath.EPSILON14); + + if (radicand < 0.0) { + // Both roots are complex. + return []; + } + + var q = -0.5 * addWithCancellationCheck(b, CesiumMath.sign(b) * Math.sqrt(radicand), CesiumMath.EPSILON14); + if (b > 0.0) { + return [q / a, c / q]; + } + + return [c / q, q / a]; + }; + + return QuadraticRealPolynomial; +}); +/*global define*/ +define('Core/CubicRealPolynomial',[ + './DeveloperError', + './QuadraticRealPolynomial' + ], function( + DeveloperError, + QuadraticRealPolynomial) { + "use strict"; + + /** + * Defines functions for 3rd order polynomial functions of one variable with only real coefficients. + * + * @exports CubicRealPolynomial + */ + var CubicRealPolynomial = {}; + + /** + * Provides the discriminant of the cubic equation from the supplied coefficients. + * @memberof CubicRealPolynomial + * + * @param {Number} a The coefficient of the 3rd order monomial. + * @param {Number} b The coefficient of the 2nd order monomial. + * @param {Number} c The coefficient of the 1st order monomial. + * @param {Number} d The coefficient of the 0th order monomial. + * @returns {Number} The value of the discriminant. + * + * @exception {DeveloperError} a is a required number. + * @exception {DeveloperError} b is a required number. + * @exception {DeveloperError} c is a required number. + * @exception {DeveloperError} d is a required number. + */ + CubicRealPolynomial.discriminant = function(a, b, c, d) { + if (typeof a !== 'number') { + throw new DeveloperError('a is a required number.'); + } + if (typeof b !== 'number') { + throw new DeveloperError('b is a required number.'); + } + if (typeof c !== 'number') { + throw new DeveloperError('c is a required number.'); + } + if (typeof d !== 'number') { + throw new DeveloperError('d is a required number.'); + } + + var a2 = a * a; + var b2 = b * b; + var c2 = c * c; + var d2 = d * d; + + var discriminant = 18.0 * a * b * c * d + b2 * c2 - 27.0 * a2 * d2 - 4.0 * (a * c2 * c + b2 * b * d); + return discriminant; + }; + + function computeRealRoots(a, b, c, d) { + var A = a; + var B = b / 3.0; + var C = c / 3.0; + var D = d; + + var AC = A * C; + var BD = B * D; + var B2 = B * B; + var C2 = C * C; + var delta1 = A * C - B2; + var delta2 = A * D - B * C; + var delta3 = B * D - C2; + + var discriminant = 4.0 * delta1 * delta3 - delta2 * delta2; + var temp; + var temp1; + + if (discriminant < 0.0) { + var ABar; + var CBar; + var DBar; + + if (B2 * BD >= AC * C2) { + ABar = A; + CBar = delta1; + DBar = -2.0 * B * delta1 + A * delta2; + } else { + ABar = D; + CBar = delta3; + DBar = -D * delta2 + 2.0 * C * delta3; + } + + var s = (DBar < 0.0) ? -1.0 : 1.0; // This is not Math.Sign()! + var temp0 = -s * Math.abs(ABar) * Math.sqrt(-discriminant); + temp1 = -DBar + temp0; + + var x = temp1 / 2.0; + var p = x < 0.0 ? -Math.pow(-x, 1.0 / 3.0) : Math.pow(x, 1.0 / 3.0); + var q = (temp1 === temp0) ? -p : -CBar / p; + + temp = (CBar <= 0.0) ? p + q : -DBar / (p * p + q * q + CBar); + + if (B2 * BD >= AC * C2) { + return [(temp - B) / A]; + } + + return [-D / (temp + C)]; + } + + var CBarA = delta1; + var DBarA = -2.0 * B * delta1 + A * delta2; + + var CBarD = delta3; + var DBarD = -D * delta2 + 2.0 * C * delta3; + + var squareRootOfDiscriminant = Math.sqrt(discriminant); + var halfSquareRootOf3 = Math.sqrt(3.0) / 2.0; + + var theta = Math.abs(Math.atan2(A * squareRootOfDiscriminant, -DBarA) / 3.0); + temp = 2.0 * Math.sqrt(-CBarA); + var cosine = Math.cos(theta); + temp1 = temp * cosine; + var temp3 = temp * (-cosine / 2.0 - halfSquareRootOf3 * Math.sin(theta)); + + var numeratorLarge = (temp1 + temp3 > 2.0 * B) ? temp1 - B : temp3 - B; + var denominatorLarge = A; + + var root1 = numeratorLarge / denominatorLarge; + + theta = Math.abs(Math.atan2(D * squareRootOfDiscriminant, -DBarD) / 3.0); + temp = 2.0 * Math.sqrt(-CBarD); + cosine = Math.cos(theta); + temp1 = temp * cosine; + temp3 = temp * (-cosine / 2.0 - halfSquareRootOf3 * Math.sin(theta)); + + var numeratorSmall = -D; + var denominatorSmall = (temp1 + temp3 < 2.0 * C) ? temp1 + C : temp3 + C; + + var root3 = numeratorSmall / denominatorSmall; + + var E = denominatorLarge * denominatorSmall; + var F = -numeratorLarge * denominatorSmall - denominatorLarge * numeratorSmall; + var G = numeratorLarge * numeratorSmall; + + var root2 = (C * F - B * G) / (-B * F + C * E); + + if (root1 <= root2) { + if (root1 <= root3) { + if (root2 <= root3) { + return [root1, root2, root3]; + } + return [root1, root3, root2]; + } + return [root3, root1, root2]; + } + if (root1 <= root3) { + return [root2, root1, root3]; + } + if (root2 <= root3) { + return [root2, root3, root1]; + } + return [root3, root2, root1]; + } + + /** + * Provides the real valued roots of the cubic polynomial with the provided coefficients. + * @memberof CubicRealPolynomial + * + * @param {Number} a The coefficient of the 3rd order monomial. + * @param {Number} b The coefficient of the 2nd order monomial. + * @param {Number} c The coefficient of the 1st order monomial. + * @param {Number} d The coefficient of the 0th order monomial. + * @returns {Array} The real valued roots. + * + * @exception {DeveloperError} a is a required number. + * @exception {DeveloperError} b is a required number. + * @exception {DeveloperError} c is a required number. + * @exception {DeveloperError} d is a required number. + */ + CubicRealPolynomial.realRoots = function(a, b, c, d) { + if (typeof a !== 'number') { + throw new DeveloperError('a is a required number.'); + } + if (typeof b !== 'number') { + throw new DeveloperError('b is a required number.'); + } + if (typeof c !== 'number') { + throw new DeveloperError('c is a required number.'); + } + if (typeof d !== 'number') { + throw new DeveloperError('d is a required number.'); + } + + var roots; + var ratio; + if (a === 0.0) { + // Quadratic function: b * x^2 + c * x + d = 0. + return QuadraticRealPolynomial.realRoots(b, c, d); + } else if (b === 0.0) { + if (c === 0.0) { + if (d === 0.0) { + // 3rd order monomial: a * x^3 = 0. + return [0.0, 0.0, 0.0]; + } + + // a * x^3 + d = 0 + ratio = -d / a; + var root = (ratio < 0.0) ? -Math.pow(-ratio, 1.0 / 3.0) : Math.pow(ratio, 1.0 / 3.0); + return [root, root, root]; + } else if (d === 0.0) { + // x * (a * x^2 + c) = 0. + roots = QuadraticRealPolynomial.realRoots(a, 0, c); + + // Return the roots in ascending order. + if (roots.Length === 0) { + return [0.0]; + } + return [roots[0], 0.0, roots[1]]; + } + + // Deflated cubic polynomial: a * x^3 + c * x + d= 0. + return computeRealRoots(a, 0, c, d); + } else if (c === 0.0) { + if (d === 0.0) { + // x^2 * (a * x + b) = 0. + ratio = -b / a; + if (ratio < 0.0) { + return [ratio, 0.0, 0.0]; + } + return [0.0, 0.0, ratio]; + } + // a * x^3 + b * x^2 + d = 0. + return computeRealRoots(a, b, 0, d); + } else if (d === 0.0) { + // x * (a * x^2 + b * x + c) = 0 + roots = QuadraticRealPolynomial.realRoots(a, b, c); + + // Return the roots in ascending order. + if (roots.length === 0) { + return [0.0]; + } else if (roots[1] <= 0.0) { + return [roots[0], roots[1], 0.0]; + } else if (roots[0] >= 0.0) { + return [0.0, roots[0], roots[1]]; + } + return [roots[0], 0.0, roots[1]]; + } + + return computeRealRoots(a, b, c, d); + }; + + return CubicRealPolynomial; +}); +/*global define*/ +define('Core/QuarticRealPolynomial',[ + './DeveloperError', + './Math', + './CubicRealPolynomial', + './QuadraticRealPolynomial' + ], + function( + DeveloperError, + CesiumMath, + CubicRealPolynomial, + QuadraticRealPolynomial) { + "use strict"; + + /** + * Defines functions for 4th order polynomial functions of one variable with only real coefficients. + * + * @exports QuarticRealPolynomial + */ + var QuarticRealPolynomial = {}; + + /** + * Provides the discriminant of the quartic equation from the supplied coefficients. + * @memberof QuarticRealPolynomial + * + * @param {Number} a The coefficient of the 4th order monomial. + * @param {Number} b The coefficient of the 3rd order monomial. + * @param {Number} c The coefficient of the 2nd order monomial. + * @param {Number} d The coefficient of the 1st order monomial. + * @param {Number} e The coefficient of the 0th order monomial. + * @returns {Number} The value of the discriminant. + * + * @exception {DeveloperError} a is a required number. + * @exception {DeveloperError} b is a required number. + * @exception {DeveloperError} c is a required number. + * @exception {DeveloperError} d is a required number. + * @exception {DeveloperError} e is a required number. + */ + QuarticRealPolynomial.discriminant = function(a, b, c, d, e) { + if (typeof a !== 'number') { + throw new DeveloperError('a is a required number.'); + } + if (typeof b !== 'number') { + throw new DeveloperError('b is a required number.'); + } + if (typeof c !== 'number') { + throw new DeveloperError('c is a required number.'); + } + if (typeof d !== 'number') { + throw new DeveloperError('d is a required number.'); + } + if (typeof e !== 'number') { + throw new DeveloperError('e is a required number.'); + } + + var a2 = a * a; + var a3 = a2 * a; + var b2 = b * b; + var b3 = b2 * b; + var c2 = c * c; + var c3 = c2 * c; + var d2 = d * d; + var d3 = d2 * d; + var e2 = e * e; + var e3 = e2 * e; + + var discriminant = (b2 * c2 * d2 - 4.0 * b3 * d3 - 4.0 * a * c3 * d2 + 18 * a * b * c * d3 - 27.0 * a2 * d2 * d2 + 256.0 * a3 * e3) + + e * (18.0 * b3 * c * d - 4.0 * b2 * c3 + 16.0 * a * c2 * c2 - 80.0 * a * b * c2 * d - 6.0 * a * b2 * d2 + 144.0 * a2 * c * d2) + + e2 * (144.0 * a * b2 * c - 27.0 * b2 * b2 - 128.0 * a2 * c2 - 192.0 * a2 * b * d); + return discriminant; + }; + + function original(a3, a2, a1, a0) { + var a3Squared = a3 * a3; + + var p = a2 - 3.0 * a3Squared / 8.0; + var q = a1 - a2 * a3 / 2.0 + a3Squared * a3 / 8.0; + var r = a0 - a1 * a3 / 4.0 + a2 * a3Squared / 16.0 - 3.0 * a3Squared * a3Squared / 256.0; + + // Find the roots of the cubic equations: h^6 + 2 p h^4 + (p^2 - 4 r) h^2 - q^2 = 0. + var cubicRoots = CubicRealPolynomial.realRoots(1.0, 2.0 * p, p * p - 4.0 * r, -q * q); + + if (cubicRoots.length > 0) { + var temp = -a3 / 4.0; + + // Use the largest positive root. + var hSquared = cubicRoots[cubicRoots.length - 1]; + + if (Math.abs(hSquared) < CesiumMath.EPSILON14) { + // y^4 + p y^2 + r = 0. + var roots = QuadraticRealPolynomial.realRoots(1.0, p, r); + + if (roots.length === 2) { + var root0 = roots[0]; + var root1 = roots[1]; + + var y; + if (root0 >= 0.0 && root1 >= 0.0) { + var y0 = Math.sqrt(root0); + var y1 = Math.sqrt(root1); + + return [temp - y1, temp - y0, temp + y0, temp + y1]; + } else if (root0 >= 0.0 && root1 < 0.0) { + y = Math.sqrt(root0); + return [temp - y, temp + y]; + } else if (root0 < 0.0 && root1 >= 0.0) { + y = Math.sqrt(root1); + return [temp - y, temp + y]; + } + } + return []; + } else if (hSquared > 0.0) { + var h = Math.sqrt(hSquared); + + var m = (p + hSquared - q / h) / 2.0; + var n = (p + hSquared + q / h) / 2.0; + + // Now solve the two quadratic factors: (y^2 + h y + m)(y^2 - h y + n); + var roots1 = QuadraticRealPolynomial.realRoots(1.0, h, m); + var roots2 = QuadraticRealPolynomial.realRoots(1.0, -h, n); + + if (roots1.length !== 0) { + roots1[0] += temp; + roots1[1] += temp; + + if (roots2.length !== 0) { + roots2[0] += temp; + roots2[1] += temp; + + if (roots1[1] <= roots2[0]) { + return [roots1[0], roots1[1], roots2[0], roots2[1]]; + } else if (roots2[1] <= roots1[0]) { + return [roots2[0], roots2[1], roots1[0], roots1[1]]; + } else if (roots1[0] >= roots2[0] && roots1[1] <= roots2[1]) { + return [roots2[0], roots1[0], roots1[1], roots2[1]]; + } else if (roots2[0] >= roots1[0] && roots2[1] <= roots1[1]) { + return [roots1[0], roots2[0], roots2[1], roots1[1]]; + } else if (roots1[0] > roots2[0] && roots1[0] < roots2[1]) { + return [roots2[0], roots1[0], roots2[1], roots1[1]]; + } + return [roots1[0], roots2[0], roots1[1], roots2[1]]; + } + return roots1; + } + + if (roots2.length !== 0) { + roots2[0] += temp; + roots2[1] += temp; + + return roots2; + } + return []; + } + } + return []; + } + + function neumark(a3, a2, a1, a0) { + var a1Squared = a1 * a1; + var a2Squared = a2 * a2; + var a3Squared = a3 * a3; + + var p = -2.0 * a2; + var q = a1 * a3 + a2Squared - 4.0 * a0; + var r = a3Squared * a0 - a1 * a2 * a3 + a1Squared; + + var cubicRoots = CubicRealPolynomial.realRoots(1.0, p, q, r); + + if (cubicRoots.length > 0) { + // Use the most positive root + var y = cubicRoots[0]; + + var temp = (a2 - y); + var tempSquared = temp * temp; + + var g1 = a3 / 2.0; + var h1 = temp / 2.0; + + var m = tempSquared - 4.0 * a0; + var mError = tempSquared + 4.0 * Math.abs(a0); + + var n = a3Squared - 4.0 * y; + var nError = a3Squared + 4.0 * Math.abs(y); + + var g2; + var h2; + + if (y < 0.0 || (m * nError < n * mError)) { + var squareRootOfN = Math.sqrt(n); + g2 = squareRootOfN / 2.0; + h2 = squareRootOfN === 0.0 ? 0.0 : (a3 * h1 - a1) / squareRootOfN; + } else { + var squareRootOfM = Math.sqrt(m); + g2 = squareRootOfM === 0.0 ? 0.0 : (a3 * h1 - a1) / squareRootOfM; + h2 = squareRootOfM / 2.0; + } + + var G; + var g; + if (g1 === 0.0 && g2 === 0.0) { + G = 0.0; + g = 0.0; + } else if (CesiumMath.sign(g1) === CesiumMath.sign(g2)) { + G = g1 + g2; + g = y / G; + } else { + g = g1 - g2; + G = y / g; + } + + var H; + var h; + if (h1 === 0.0 && h2 === 0.0) { + H = 0.0; + h = 0.0; + } else if (CesiumMath.sign(h1) === CesiumMath.sign(h2)) { + H = h1 + h2; + h = a0 / H; + } else { + h = h1 - h2; + H = a0 / h; + } + + // Now solve the two quadratic factors: (y^2 + G y + H)(y^2 + g y + h); + var roots1 = QuadraticRealPolynomial.realRoots(1.0, G, H); + var roots2 = QuadraticRealPolynomial.realRoots(1.0, g, h); + + if (roots1.length !== 0) { + if (roots2.length !== 0) { + if (roots1[1] <= roots2[0]) { + return [roots1[0], roots1[1], roots2[0], roots2[1]]; + } else if (roots2[1] <= roots1[0]) { + return [roots2[0], roots2[1], roots1[0], roots1[1]]; + } else if (roots1[0] >= roots2[0] && roots1[1] <= roots2[1]) { + return [roots2[0], roots1[0], roots1[1], roots2[1]]; + } else if (roots2[0] >= roots1[0] && roots2[1] <= roots1[1]) { + return [roots1[0], roots2[0], roots2[1], roots1[1]]; + } else if (roots1[0] > roots2[0] && roots1[0] < roots2[1]) { + return [roots2[0], roots1[0], roots2[1], roots1[1]]; + } else { + return [roots1[0], roots2[0], roots1[1], roots2[1]]; + } + } + return roots1; + } + if (roots2.length !== 0) { + return roots2; + } + } + return []; + } + + /** + * Provides the real valued roots of the quartic polynomial with the provided coefficients. + * @memberof QuarticRealPolynomial + * + * @param {Number} a The coefficient of the 4th order monomial. + * @param {Number} b The coefficient of the 3rd order monomial. + * @param {Number} c The coefficient of the 2nd order monomial. + * @param {Number} d The coefficient of the 1st order monomial. + * @param {Number} e The coefficient of the 0th order monomial. + * @returns {Array} The real valued roots. + * + * @exception {DeveloperError} a is a required number. + * @exception {DeveloperError} b is a required number. + * @exception {DeveloperError} c is a required number. + * @exception {DeveloperError} d is a required number. + * @exception {DeveloperError} e is a required number. + */ + QuarticRealPolynomial.realRoots = function(a, b, c, d, e) { + if (typeof a !== 'number') { + throw new DeveloperError('a is a required number.'); + } + if (typeof b !== 'number') { + throw new DeveloperError('b is a required number.'); + } + if (typeof c !== 'number') { + throw new DeveloperError('c is a required number.'); + } + if (typeof d !== 'number') { + throw new DeveloperError('d is a required number.'); + } + if (typeof e !== 'number') { + throw new DeveloperError('e is a required number.'); + } + + if (Math.abs(a) < CesiumMath.EPSILON15) { + return CubicRealPolynomial.realRoots(b, c, d, e); + } + var a3 = b / a; + var a2 = c / a; + var a1 = d / a; + var a0 = e / a; + + var k = (a3 < 0.0) ? 1 : 0; + k += (a2 < 0.0) ? k + 1 : k; + k += (a1 < 0.0) ? k + 1 : k; + k += (a0 < 0.0) ? k + 1 : k; + + switch (k) { + case 0: + return original(a3, a2, a1, a0); + case 1: + return neumark(a3, a2, a1, a0); + case 2: + return neumark(a3, a2, a1, a0); + case 3: + return original(a3, a2, a1, a0); + case 4: + return original(a3, a2, a1, a0); + case 5: + return neumark(a3, a2, a1, a0); + case 6: + return original(a3, a2, a1, a0); + case 7: + return original(a3, a2, a1, a0); + case 8: + return neumark(a3, a2, a1, a0); + case 9: + return original(a3, a2, a1, a0); + case 10: + return original(a3, a2, a1, a0); + case 11: + return neumark(a3, a2, a1, a0); + case 12: + return original(a3, a2, a1, a0); + case 13: + return original(a3, a2, a1, a0); + case 14: + return original(a3, a2, a1, a0); + case 15: + return original(a3, a2, a1, a0); + default: + return undefined; + } + }; + + return QuarticRealPolynomial; +}); +/*global define*/ +define('Core/IntersectionTests',[ + './defined', + './DeveloperError', + './Math', + './Cartesian3', + './Cartographic', + './Matrix3', + './QuadraticRealPolynomial', + './QuarticRealPolynomial' + ], + function( + defined, + DeveloperError, + CesiumMath, + Cartesian3, + Cartographic, + Matrix3, + QuadraticRealPolynomial, + QuarticRealPolynomial) { + "use strict"; + + /** + * DOC_TBA + * + * @exports IntersectionTests + */ + var IntersectionTests = {}; + + /** + * Computes the intersection of a ray and a plane. + * @memberof IntersectionTests + * + * @param {Ray} ray The ray. + * @param {Plane} plane The plane. + * @returns {Cartesian3} The intersection point or undefined if there is no intersections. + * + * @exception {DeveloperError} ray is required. + * @exception {DeveloperError} plane is required. + */ + IntersectionTests.rayPlane = function(ray, plane, result) { + if (!defined(ray)) { + throw new DeveloperError('ray is required.'); + } + + if (!defined(plane)) { + throw new DeveloperError('plane is required.'); + } + + var origin = ray.origin; + var direction = ray.direction; + var normal = plane.normal; + var denominator = Cartesian3.dot(normal, direction); + + if (Math.abs(denominator) < CesiumMath.EPSILON15) { + // Ray is parallel to plane. The ray may be in the polygon's plane. + return undefined; + } + + var t = (-plane.distance - Cartesian3.dot(normal, origin)) / denominator; + + if (t < 0) { + return undefined; + } + + result = Cartesian3.multiplyByScalar(direction, t, result); + return Cartesian3.add(origin, result, result); + }; + + var scratchQ = new Cartesian3(); + var scratchW = new Cartesian3(); + + /** + * Computes the intersection points of a ray with an ellipsoid. + * @memberof IntersectionTests + * + * @param {Ray} ray The ray. + * @param {Ellipsoid} ellipsoid The ellipsoid. + * @returns {Object} An object with the first (<code>start</code>) and the second (<code>stop</code>) intersection scalars for points along the ray or undefined if there are no intersections. + * + * @exception {DeveloperError} ray is required. + * @exception {DeveloperError} ellipsoid is required. + */ + IntersectionTests.rayEllipsoid = function(ray, ellipsoid) { + if (!defined(ray)) { + throw new DeveloperError('ray is required.'); + } + + if (!defined(ellipsoid)) { + throw new DeveloperError('ellipsoid is required.'); + } + + var inverseRadii = ellipsoid.getOneOverRadii(); + var q = Cartesian3.multiplyComponents(inverseRadii, ray.origin, scratchQ); + var w = Cartesian3.multiplyComponents(inverseRadii, ray.direction, scratchW); + + var q2 = Cartesian3.magnitudeSquared(q); + var qw = Cartesian3.dot(q, w); + + var difference, w2, product, discriminant, temp; + + if (q2 > 1.0) { + // Outside ellipsoid. + if (qw >= 0.0) { + // Looking outward or tangent (0 intersections). + return undefined; + } + + // qw < 0.0. + var qw2 = qw * qw; + difference = q2 - 1.0; // Positively valued. + w2 = Cartesian3.magnitudeSquared(w); + product = w2 * difference; + + if (qw2 < product) { + // Imaginary roots (0 intersections). + return undefined; + } else if (qw2 > product) { + // Distinct roots (2 intersections). + discriminant = qw * qw - product; + temp = -qw + Math.sqrt(discriminant); // Avoid cancellation. + var root0 = temp / w2; + var root1 = difference / temp; + if (root0 < root1) { + return { + start : root0, + stop : root1 + }; + } + + return { + start : root1, + stop : root0 + }; + } else { + // qw2 == product. Repeated roots (2 intersections). + var root = Math.sqrt(difference / w2); + return { + start : root, + stop : root + }; + } + } else if (q2 < 1.0) { + // Inside ellipsoid (2 intersections). + difference = q2 - 1.0; // Negatively valued. + w2 = Cartesian3.magnitudeSquared(w); + product = w2 * difference; // Negatively valued. + + discriminant = qw * qw - product; + temp = -qw + Math.sqrt(discriminant); // Positively valued. + return { + start : 0.0, + stop : temp / w2 + }; + } else { + // q2 == 1.0. On ellipsoid. + if (qw < 0.0) { + // Looking inward. + w2 = Cartesian3.magnitudeSquared(w); + return { + start : 0.0, + stop : -qw / w2 + }; + } + + // qw >= 0.0. Looking outward or tangent. + return undefined; + } + }; + + function addWithCancellationCheck(left, right, tolerance) { + var difference = left + right; + if ((CesiumMath.sign(left) !== CesiumMath.sign(right)) && + Math.abs(difference / Math.max(Math.abs(left), Math.abs(right))) < tolerance) { + return 0.0; + } + + return difference; + } + + function quadraticVectorExpression(A, b, c, x, w) { + var xSquared = x * x; + var wSquared = w * w; + + var l2 = (A[Matrix3.COLUMN1ROW1] - A[Matrix3.COLUMN2ROW2]) * wSquared; + var l1 = w * (x * addWithCancellationCheck(A[Matrix3.COLUMN1ROW0], A[Matrix3.COLUMN0ROW1], CesiumMath.EPSILON15) + b.y); + var l0 = (A[Matrix3.COLUMN0ROW0] * xSquared + A[Matrix3.COLUMN2ROW2] * wSquared) + x * b.x + c; + + var r1 = wSquared * addWithCancellationCheck(A[Matrix3.COLUMN2ROW1], A[Matrix3.COLUMN1ROW2], CesiumMath.EPSILON15); + var r0 = w * (x * addWithCancellationCheck(A[Matrix3.COLUMN2ROW0], A[Matrix3.COLUMN0ROW2]) + b.z); + + var cosines; + var solutions = []; + if (r0 === 0.0 && r1 === 0.0) { + cosines = QuadraticRealPolynomial.realRoots(l2, l1, l0); + if (cosines.length === 0) { + return solutions; + } + + var cosine0 = cosines[0]; + var sine0 = Math.sqrt(Math.max(1.0 - cosine0 * cosine0, 0.0)); + solutions.push(new Cartesian3(x, w * cosine0, w * -sine0)); + solutions.push(new Cartesian3(x, w * cosine0, w * sine0)); + + if (cosines.length === 2) { + var cosine1 = cosines[1]; + var sine1 = Math.sqrt(Math.max(1.0 - cosine1 * cosine1, 0.0)); + solutions.push(new Cartesian3(x, w * cosine1, w * -sine1)); + solutions.push(new Cartesian3(x, w * cosine1, w * sine1)); + } + + return solutions; + } + + var r0Squared = r0 * r0; + var r1Squared = r1 * r1; + var l2Squared = l2 * l2; + var r0r1 = r0 * r1; + + var c4 = l2Squared + r1Squared; + var c3 = 2.0 * (l1 * l2 + r0r1); + var c2 = 2.0 * l0 * l2 + l1 * l1 - r1Squared + r0Squared; + var c1 = 2.0 * (l0 * l1 - r0r1); + var c0 = l0 * l0 - r0Squared; + + if (c4 === 0.0 && c3 === 0.0 && c2 === 0.0 && c1 === 0.0) { + return solutions; + } + + cosines = QuarticRealPolynomial.realRoots(c4, c3, c2, c1, c0); + var length = cosines.length; + if (length === 0) { + return solutions; + } + + for ( var i = 0; i < length; ++i) { + var cosine = cosines[i]; + var cosineSquared = cosine * cosine; + var sineSquared = Math.max(1.0 - cosineSquared, 0.0); + var sine = Math.sqrt(sineSquared); + + //var left = l2 * cosineSquared + l1 * cosine + l0; + var left; + if (CesiumMath.sign(l2) === CesiumMath.sign(l0)) { + left = addWithCancellationCheck(l2 * cosineSquared + l0, l1 * cosine, CesiumMath.EPSILON12); + } else if (CesiumMath.sign(l0) === CesiumMath.sign(l1 * cosine)) { + left = addWithCancellationCheck(l2 * cosineSquared, l1 * cosine + l0, CesiumMath.EPSILON12); + } else { + left = addWithCancellationCheck(l2 * cosineSquared + l1 * cosine, l0, CesiumMath.EPSILON12); + } + + var right = addWithCancellationCheck(r1 * cosine, r0, CesiumMath.EPSILON15); + var product = left * right; + + if (product < 0.0) { + solutions.push(new Cartesian3(x, w * cosine, w * sine)); + } else if (product > 0.0) { + solutions.push(new Cartesian3(x, w * cosine, w * -sine)); + } else if (sine !== 0.0) { + solutions.push(new Cartesian3(x, w * cosine, w * -sine)); + solutions.push(new Cartesian3(x, w * cosine, w * sine)); + ++i; + } else { + solutions.push(new Cartesian3(x, w * cosine, w * sine)); + } + } + + return solutions; + } + + /** + * Provides the point along the ray which is nearest to the ellipsoid. + * @memberof IntersectionTests + * + * @param {Ray} ray The ray. + * @param {Ellipsoid} ellipsoid The ellipsoid. + * @returns {Cartesian} The nearest planetodetic point on the ray. + * + * @exception {DeveloperError} ray is required. + * @exception {DeveloperError} ellipsoid is required. + */ + IntersectionTests.grazingAltitudeLocation = function(ray, ellipsoid) { + if (!defined(ray)) { + throw new DeveloperError('ray is required.'); + } + + if (!defined(ellipsoid)) { + throw new DeveloperError('ellipsoid is required.'); + } + + var position = ray.origin; + var direction = ray.direction; + + var normal = ellipsoid.geodeticSurfaceNormal(position); + + if (Cartesian3.dot(direction, normal) >= 0.0) { // The location provided is the closest point in altitude + return position; + } + + var intersects = defined(this.rayEllipsoid(ray, ellipsoid)); + + // Compute the scaled direction vector. + var f = ellipsoid.transformPositionToScaledSpace(direction); + + // Constructs a basis from the unit scaled direction vector. Construct its rotation and transpose. + var firstAxis = Cartesian3.normalize(f); + var reference = Cartesian3.mostOrthogonalAxis(f); + var secondAxis = Cartesian3.normalize(Cartesian3.cross(reference, firstAxis)); + var thirdAxis = Cartesian3.normalize(Cartesian3.cross(firstAxis, secondAxis)); + var B = new Matrix3(firstAxis.x, secondAxis.x, thirdAxis.x, + firstAxis.y, secondAxis.y, thirdAxis.y, + firstAxis.z, secondAxis.z, thirdAxis.z); + var B_T = Matrix3.transpose(B); + + // Get the scaling matrix and its inverse. + var D_I = Matrix3.fromScale(ellipsoid.getRadii()); + var D = Matrix3.fromScale(ellipsoid.getOneOverRadii()); + + var C = new Matrix3(0.0, direction.z, -direction.y, + -direction.z, 0.0, direction.x, + direction.y, -direction.x, 0.0); + + var temp = Matrix3.multiply(Matrix3.multiply(B_T, D), C); + var A = Matrix3.multiply(Matrix3.multiply(temp, D_I), B); + var b = Matrix3.multiplyByVector(temp, position); + + // Solve for the solutions to the expression in standard form: + var solutions = quadraticVectorExpression(A, Cartesian3.negate(b), 0.0, 0.0, 1.0); + + var s; + var altitude; + var length = solutions.length; + if (length > 0) { + var closest = Cartesian3.ZERO; + var maximumValue = Number.NEGATIVE_INFINITY; + + for ( var i = 0; i < length; ++i) { + s = Matrix3.multiplyByVector(D_I, Matrix3.multiplyByVector(B, solutions[i])); + var v = Cartesian3.normalize(Cartesian3.subtract(s, position)); + var dotProduct = Cartesian3.dot(v, direction); + + if (dotProduct > maximumValue) { + maximumValue = dotProduct; + closest = s; + } + } + + var surfacePoint = ellipsoid.cartesianToCartographic(closest); + maximumValue = CesiumMath.clamp(maximumValue, 0.0, 1.0); + altitude = Cartesian3.magnitude(Cartesian3.subtract(closest, position)) * Math.sqrt(1.0 - maximumValue * maximumValue); + altitude = intersects ? -altitude : altitude; + return ellipsoid.cartographicToCartesian(new Cartographic(surfacePoint.longitude, surfacePoint.latitude, altitude)); + } + + return undefined; + }; + + var lineSegmentPlaneDifference = new Cartesian3(); + + /** + * Computes the intersection of a line segment and a plane. + * @memberof IntersectionTests + * + * @param {Cartesian3} endPoint0 An end point of the line segment. + * @param {Cartesian3} endPoint1 The other end point of the line segment. + * @param {Plane} plane The plane. + * @param {Cartesian3} [result] The object onto which to store the result. + * @returns {Cartesian3} The intersection point or undefined if there is no intersection. + * + * @exception {DeveloperError} endPoint0 is required. + * @exception {DeveloperError} endPoint1 is required. + * @exception {DeveloperError} plane is required. + * + * @example + * var origin = ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-75.59777, 40.03883, 0.0)); + * var normal = ellipsoid.geodeticSurfaceNormal(origin); + * var plane = Plane.fromPointNormal(origin, normal); + * + * var p0 = new Cartesian3(...); + * var p1 = new Cartesian3(...); + * + * // find the intersection of the line segment from p0 to p1 and the tangent plane at origin. + * var intersection = IntersectionTests.lineSegmentPlane(p0, p1, plane); + */ + IntersectionTests.lineSegmentPlane = function(endPoint0, endPoint1, plane, result) { + if (!defined(endPoint0)) { + throw new DeveloperError('endPoint0 is required.'); + } + + if (!defined(endPoint1)) { + throw new DeveloperError('endPoint1 is required.'); + } + + if (!defined(plane)) { + throw new DeveloperError('plane is required.'); + } + + var difference = Cartesian3.subtract(endPoint1, endPoint0, lineSegmentPlaneDifference); + var normal = plane.normal; + var nDotDiff = Cartesian3.dot(normal, difference); + + // check if the segment and plane are parallel + if (Math.abs(nDotDiff) < CesiumMath.EPSILON6) { + return undefined; + } + + var nDotP0 = Cartesian3.dot(normal, endPoint0); + var t = -(plane.distance + nDotP0) / nDotDiff; + + // intersection only if t is in [0, 1] + if (t < 0.0 || t > 1.0) { + return undefined; + } + + // intersection is endPoint0 + t * (endPoint1 - endPoint0) + if (!defined(result)) { + result = new Cartesian3(); + } + Cartesian3.multiplyByScalar(difference, t, result); + Cartesian3.add(endPoint0, result, result); + return result; + }; + + /** + * Computes the intersection of a triangle and a plane + * @memberof IntersectionTests + * + * @param {Cartesian3} p0 First point of the triangle + * @param {Cartesian3} p1 Second point of the triangle + * @param {Cartesian3} p2 Third point of the triangle + * @param {Plane} plane Intersection plane + * + * @returns {Object} An object with properties <code>positions</code> and <code>indices</code>, which are arrays that represent three triangles that do not cross the plane. (Undefined if no intersection exists) + * + * @exception {DeveloperError} p0, p1, p2, and plane are required. + * + * @example + * var origin = ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-75.59777, 40.03883, 0.0)); + * var normal = ellipsoid.geodeticSurfaceNormal(origin); + * var plane = Plane.fromPointNormal(origin, normal); + * + * var p0 = new Cartesian3(...); + * var p1 = new Cartesian3(...); + * var p2 = new Cartesian3(...); + * + * // convert the triangle composed of points (p0, p1, p2) to three triangles that don't cross the plane + * var triangles = IntersectionTests.lineSegmentPlane(p0, p1, p2, plane); + * + */ + IntersectionTests.trianglePlaneIntersection = function(p0, p1, p2, plane) { + if ((!defined(p0)) || + (!defined(p1)) || + (!defined(p2)) || + (!defined(plane))) { + throw new DeveloperError('p0, p1, p2, and plane are required.'); + } + + var planeNormal = plane.normal; + var planeD = plane.distance; + var p0Behind = (Cartesian3.dot(planeNormal, p0) + planeD) < 0.0; + var p1Behind = (Cartesian3.dot(planeNormal, p1) + planeD) < 0.0; + var p2Behind = (Cartesian3.dot(planeNormal, p2) + planeD) < 0.0; + // Given these dots products, the calls to lineSegmentPlaneIntersection + // always have defined results. + + var numBehind = 0; + numBehind += p0Behind ? 1 : 0; + numBehind += p1Behind ? 1 : 0; + numBehind += p2Behind ? 1 : 0; + + var u1, u2; + if (numBehind === 1 || numBehind === 2) { + u1 = new Cartesian3(); + u2 = new Cartesian3(); + } + + if (numBehind === 1) { + if (p0Behind) { + IntersectionTests.lineSegmentPlane(p0, p1, plane, u1); + IntersectionTests.lineSegmentPlane(p0, p2, plane, u2); + + return { + positions : [p0, p1, p2, u1, u2 ], + indices : [ + // Behind + 0, 3, 4, + + // In front + 1, 2, 4, + 1, 4, 3 + ] + }; + } else if (p1Behind) { + IntersectionTests.lineSegmentPlane(p1, p2, plane, u1); + IntersectionTests.lineSegmentPlane(p1, p0, plane, u2); + + return { + positions : [p0, p1, p2, u1, u2 ], + indices : [ + // Behind + 1, 3, 4, + + // In front + 2, 0, 4, + 2, 4, 3 + ] + }; + } else if (p2Behind) { + IntersectionTests.lineSegmentPlane(p2, p0, plane, u1); + IntersectionTests.lineSegmentPlane(p2, p1, plane, u2); + + return { + positions : [p0, p1, p2, u1, u2 ], + indices : [ + // Behind + 2, 3, 4, + + // In front + 0, 1, 4, + 0, 4, 3 + ] + }; + } + } else if (numBehind === 2) { + if (!p0Behind) { + IntersectionTests.lineSegmentPlane(p1, p0, plane, u1); + IntersectionTests.lineSegmentPlane(p2, p0, plane, u2); + + return { + positions : [p0, p1, p2, u1, u2 ], + indices : [ + // Behind + 1, 2, 4, + 1, 4, 3, + + // In front + 0, 3, 4 + ] + }; + } else if (!p1Behind) { + IntersectionTests.lineSegmentPlane(p2, p1, plane, u1); + IntersectionTests.lineSegmentPlane(p0, p1, plane, u2); + + return { + positions : [p0, p1, p2, u1, u2 ], + indices : [ + // Behind + 2, 0, 4, + 2, 4, 3, + + // In front + 1, 3, 4 + ] + }; + } else if (!p2Behind) { + IntersectionTests.lineSegmentPlane(p0, p2, plane, u1); + IntersectionTests.lineSegmentPlane(p1, p2, plane, u2); + + return { + positions : [p0, p1, p2, u1, u2 ], + indices : [ + // Behind + 0, 1, 4, + 0, 4, 3, + + // In front + 2, 3, 4 + ] + }; + } + } + + // if numBehind is 3, the triangle is completely behind the plane; + // otherwise, it is completely in front (numBehind is 0). + return undefined; + }; + + return IntersectionTests; +}); + +/*global define*/ +define('Core/Plane',[ + './Cartesian3', + './defined', + './DeveloperError' + ], function( + Cartesian3, + defined, + DeveloperError) { + "use strict"; + + /** + * A plane in Hessian Normal Form defined by + * <pre> + * ax + by + cz + d = 0 + * </pre> + * where (a, b, c) is the plane's <code>normal</code>, d is the signed + * <code>distance</code> to the plane, and (x, y, z) is any point on + * the plane. + * + * @alias Plane + * @constructor + * + * @param {Cartesian3} normal The plane's normal (normalized). + * @param {Number} distance The shortest distance from the origin to the plane. The sign of + * <code>distance</code> determines which side of the plane the origin + * is on. If <code>distance</code> is positive, the origin is in the half-space + * in the direction of the normal; if negative, the origin is in the half-space + * opposite to the normal; if zero, the plane passes through the origin. + * + * @exception {DeveloperError} normal is required. + * @exception {DeveloperError} distance is required. + * + * @example + * // The plane x=0 + * var plane = new Plane(Cartesian3.UNIT_X, 0.0); + */ + var Plane = function(normal, distance) { + if (!defined(normal)) { + throw new DeveloperError('normal is required.'); + } + + if (!defined(distance)) { + throw new DeveloperError('distance is required.'); + } + + /** + * The plane's normal. + * + * @type {Cartesian3} + */ + this.normal = Cartesian3.clone(normal); + + /** + * The shortest distance from the origin to the plane. The sign of + * <code>distance</code> determines which side of the plane the origin + * is on. If <code>distance</code> is positive, the origin is in the half-space + * in the direction of the normal; if negative, the origin is in the half-space + * opposite to the normal; if zero, the plane passes through the origin. + * + * @type {Number} + */ + this.distance = distance; + }; + + /** + * Creates a plane from a normal and a point on the plane. + * @memberof Plane + * + * @param {Cartesian3} point The point on the plane. + * @param {Cartesian3} normal The plane's normal (normalized). + * @param {Plane} [result] The object onto which to store the result. + * @returns {Plane} A new plane instance or the modified result parameter. + * + * @exception {DeveloperError} point is required. + * @exception {DeveloperError} normal is required. + * + * @example + * var point = ellipsoid.cartographicToCartesian(Cartographic.fromDegrees(-72.0, 40.0)); + * var normal = ellipsoid.geodeticSurfaceNormal(point); + * var tangentPlane = Plane.fromPointNormal(point, normal); + */ + Plane.fromPointNormal = function(point, normal, result) { + if (!defined(point)) { + throw new DeveloperError('point is required.'); + } + + if (!defined(normal)) { + throw new DeveloperError('normal is required.'); + } + + var distance = -Cartesian3.dot(normal, point); + + if (!defined(result)) { + return new Plane(normal, distance); + } + + Cartesian3.clone(normal, result.normal); + result.distance = distance; + return result; + }; + + /** + * Computes the signed shortest distance of a point to a plane. + * The sign of the distance determines which side of the plane the point + * is on. If the distance is positive, the point is in the half-space + * in the direction of the normal; if negative, the point is in the half-space + * opposite to the normal; if zero, the plane passes through the point. + * @memberof Plane + * + * @param {Plane} plane The plane. + * @param {Cartesian3} point The point. + * @returns {Number} The signed shortest distance of the point to the plane. + * + * @exception {DeveloperError} plane is required. + * @exception {DeveloperError} point is required. + */ + Plane.getPointDistance = function(plane, point) { + if (!defined(plane)) { + throw new DeveloperError('plane is required.'); + } + + if (!defined(point)) { + throw new DeveloperError('point is required.'); + } + + return Cartesian3.dot(plane.normal, point) + plane.distance; + }; + + + /** + * Computes the signed shortest distance of a point to this plane. + * The sign of the distance determines which side of this plane the point + * is on. If the distance is positive, the point is in the half-space + * in the direction of the normal; if negative, the point is in the half-space + * opposite to the normal; if zero, this plane passes through the point. + * @memberof Plane + * + * @param {Cartesian3} point The point. + * @returns {Number} The signed shortest distance of the point to this plane. + * + * @exception {DeveloperError} point is required. + */ + Plane.prototype.getPointDistance = function(point) { + return Plane.getPointDistance(this, point); + }; + + return Plane; +}); + +/*global define*/ +define('Core/Tipsify',[ + './defaultValue', + './defined', + './DeveloperError' + ], function( + defaultValue, + defined, + DeveloperError) { + "use strict"; + + /** + * Encapsulates an algorithm to optimize triangles for the post + * vertex-shader cache. This is based on the 2007 SIGGRAPH paper + * 'Fast Triangle Reordering for Vertex Locality and Reduced Overdraw.' + * The runtime is linear but several passes are made. + * + * @exports Tipsify + * + * @see <a href='http://gfx.cs.princeton.edu/pubs/Sander_2007_%3ETR/tipsy.pdf'> + * Fast Triangle Reordering for Vertex Locality and Reduced Overdraw</a> + * by Sander, Nehab, and Barczak + */ + var Tipsify = {}; + + /** + * Calculates the average cache miss ratio (ACMR) for a given set of indices. + * + * @param {Array} description.indices Lists triads of numbers corresponding to the indices of the vertices + * in the vertex buffer that define the geometry's triangles. + * @param {Number} [description.maximumIndex] The maximum value of the elements in <code>args.indices</code>. + * If not supplied, this value will be computed. + * @param {Number} [description.cacheSize=24] The number of vertices that can be stored in the cache at any one time. + * + * @exception {DeveloperError} indices is required. + * @exception {DeveloperError} indices length must be a multiple of three. + * @exception {DeveloperError} cacheSize must be greater than two. + * + * @returns {Number} The average cache miss ratio (ACMR). + * + * @example + * var indices = [0, 1, 2, 3, 4, 5]; + * var maxIndex = 5; + * var cacheSize = 3; + * var acmr = Tipsify.calculateACMR({indices : indices, maxIndex : maxIndex, cacheSize : cacheSize}); + */ + Tipsify.calculateACMR = function(description) { + description = defaultValue(description, defaultValue.EMPTY_OBJECT); + var indices = description.indices; + var maximumIndex = description.maximumIndex; + var cacheSize = defaultValue(description.cacheSize, 24); + + if (!defined(indices)) { + throw new DeveloperError('indices is required.'); + } + + var numIndices = indices.length; + + if (numIndices < 3 || numIndices % 3 !== 0) { + throw new DeveloperError('indices length must be a multiple of three.'); + } + if (maximumIndex <= 0) { + throw new DeveloperError('maximumIndex must be greater than zero.'); + } + if (cacheSize < 3) { + throw new DeveloperError('cacheSize must be greater than two.'); + } + + // Compute the maximumIndex if not given + if (!defined(maximumIndex)) { + maximumIndex = 0; + var currentIndex = 0; + var intoIndices = indices[currentIndex]; + while (currentIndex < numIndices) { + if (intoIndices > maximumIndex) { + maximumIndex = intoIndices; + } + ++currentIndex; + intoIndices = indices[currentIndex]; + } + } + + // Vertex time stamps + var vertexTimeStamps = []; + for ( var i = 0; i < maximumIndex + 1; i++) { + vertexTimeStamps[i] = 0; + } + + // Cache processing + var s = cacheSize + 1; + for ( var j = 0; j < numIndices; ++j) { + if ((s - vertexTimeStamps[indices[j]]) > cacheSize) { + vertexTimeStamps[indices[j]] = s; + ++s; + } + } + + return (s - cacheSize + 1) / (numIndices / 3); + }; + + /** + * Optimizes triangles for the post-vertex shader cache. + * + * @param {Array} description.indices Lists triads of numbers corresponding to the indices of the vertices + * in the vertex buffer that define the geometry's triangles. + * @param {Number} [description.maximumIndex] The maximum value of the elements in <code>args.indices</code>. + * If not supplied, this value will be computed. + * @param {Number} [description.cacheSize=24] The number of vertices that can be stored in the cache at any one time. + * + * @exception {DeveloperError} indices is required. + * @exception {DeveloperError} indices length must be a multiple of three. + * @exception {DeveloperError} cacheSize must be greater than two. + * + * @returns {Array} A list of the input indices in an optimized order. + * + * @example + * var indices = [0, 1, 2, 3, 4, 5]; + * var maxIndex = 5; + * var cacheSize = 3; + * var reorderedIndices = Tipsify.tipsify({indices : indices, maxIndex : maxIndex, cacheSize : cacheSize}); + */ + Tipsify.tipsify = function(description) { + description = defaultValue(description, defaultValue.EMPTY_OBJECT); + var indices = description.indices; + var maximumIndex = description.maximumIndex; + var cacheSize = defaultValue(description.cacheSize, 24); + + var cursor; + + function skipDeadEnd(vertices, deadEnd, indices, maximumIndexPlusOne) { + while (deadEnd.length >= 1) { + // while the stack is not empty + var d = deadEnd[deadEnd.length - 1]; // top of the stack + deadEnd.splice(deadEnd.length - 1, 1); // pop the stack + + if (vertices[d].numLiveTriangles > 0) { + return d; + } + } + + while (cursor < maximumIndexPlusOne) { + if (vertices[cursor].numLiveTriangles > 0) { + ++cursor; + return cursor - 1; + } + ++cursor; + } + return -1; + } + + function getNextVertex(indices, cacheSize, oneRing, vertices, s, deadEnd, maximumIndexPlusOne) { + var n = -1; + var p; + var m = -1; + var itOneRing = 0; + while (itOneRing < oneRing.length) { + var index = oneRing[itOneRing]; + if (vertices[index].numLiveTriangles) { + p = 0; + if ((s - vertices[index].timeStamp + (2 * vertices[index].numLiveTriangles)) <= cacheSize) { + p = s - vertices[index].timeStamp; + } + if ((p > m) || (m === -1)) { + m = p; + n = index; + } + } + ++itOneRing; + } + if (n === -1) { + return skipDeadEnd(vertices, deadEnd, indices, maximumIndexPlusOne); + } + return n; + } + + if (!defined(indices)) { + throw new DeveloperError('indices is required.'); + } + var numIndices = indices.length; + + if (numIndices < 3 || numIndices % 3 !== 0) { + throw new DeveloperError('indices length must be a multiple of three.'); + } + if (maximumIndex <= 0) { + throw new DeveloperError('maximumIndex must be greater than zero.'); + } + if (cacheSize < 3) { + throw new DeveloperError('cacheSize must be greater than two.'); + } + + // Determine maximum index + var maximumIndexPlusOne = 0; + var currentIndex = 0; + var intoIndices = indices[currentIndex]; + var endIndex = numIndices; + if (defined(maximumIndex)) { + maximumIndexPlusOne = maximumIndex + 1; + } else { + while (currentIndex < endIndex) { + if (intoIndices > maximumIndexPlusOne) { + maximumIndexPlusOne = intoIndices; + } + ++currentIndex; + intoIndices = indices[currentIndex]; + } + if (maximumIndexPlusOne === -1) { + return 0; + } + ++maximumIndexPlusOne; + } + + // Vertices + var vertices = []; + for ( var i = 0; i < maximumIndexPlusOne; i++) { + vertices[i] = { + numLiveTriangles : 0, + timeStamp : 0, + vertexTriangles : [] + }; + } + currentIndex = 0; + var triangle = 0; + while (currentIndex < endIndex) { + vertices[indices[currentIndex]].vertexTriangles.push(triangle); + ++(vertices[indices[currentIndex]]).numLiveTriangles; + vertices[indices[currentIndex + 1]].vertexTriangles.push(triangle); + ++(vertices[indices[currentIndex + 1]]).numLiveTriangles; + vertices[indices[currentIndex + 2]].vertexTriangles.push(triangle); + ++(vertices[indices[currentIndex + 2]]).numLiveTriangles; + ++triangle; + currentIndex += 3; + } + + // Starting index + var f = 0; + + // Time Stamp + var s = cacheSize + 1; + cursor = 1; + + // Process + var oneRing = []; + var deadEnd = []; //Stack + var vertex; + var intoVertices; + var currentOutputIndex = 0; + var outputIndices = []; + var numTriangles = numIndices / 3; + var triangleEmitted = []; + for (i = 0; i < numTriangles; i++) { + triangleEmitted[i] = false; + } + var index; + var limit; + while (f !== -1) { + oneRing = []; + intoVertices = vertices[f]; + limit = intoVertices.vertexTriangles.length; + for ( var k = 0; k < limit; ++k) { + triangle = intoVertices.vertexTriangles[k]; + if (!triangleEmitted[triangle]) { + triangleEmitted[triangle] = true; + currentIndex = triangle + triangle + triangle; + for ( var j = 0; j < 3; ++j) { + // Set this index as a possible next index + index = indices[currentIndex]; + oneRing.push(index); + deadEnd.push(index); + + // Output index + outputIndices[currentOutputIndex] = index; + ++currentOutputIndex; + + // Cache processing + vertex = vertices[index]; + --vertex.numLiveTriangles; + if ((s - vertex.timeStamp) > cacheSize) { + vertex.timeStamp = s; + ++s; + } + ++currentIndex; + } + } + } + f = getNextVertex(indices, cacheSize, oneRing, vertices, s, deadEnd, maximumIndexPlusOne); + } + + return outputIndices; + }; + + return Tipsify; +}); + +/*global define*/ +define('Core/GeometryPipeline',[ + './barycentricCoordinates', + './defaultValue', + './defined', + './DeveloperError', + './Cartesian2', + './Cartesian3', + './Cartesian4', + './Cartographic', + './EncodedCartesian3', + './Intersect', + './IntersectionTests', + './Math', + './Matrix3', + './Matrix4', + './Plane', + './GeographicProjection', + './ComponentDatatype', + './IndexDatatype', + './PrimitiveType', + './Tipsify', + './BoundingSphere', + './Geometry', + './GeometryAttribute' + ], function( + barycentricCoordinates, + defaultValue, + defined, + DeveloperError, + Cartesian2, + Cartesian3, + Cartesian4, + Cartographic, + EncodedCartesian3, + Intersect, + IntersectionTests, + CesiumMath, + Matrix3, + Matrix4, + Plane, + GeographicProjection, + ComponentDatatype, + IndexDatatype, + PrimitiveType, + Tipsify, + BoundingSphere, + Geometry, + GeometryAttribute) { + "use strict"; + + /** + * Content pipeline functions for geometries. + * + * @exports GeometryPipeline + * + * @see Geometry + * @see Context#createVertexArrayFromGeometry + */ + var GeometryPipeline = {}; + + function addTriangle(lines, index, i0, i1, i2) { + lines[index++] = i0; + lines[index++] = i1; + + lines[index++] = i1; + lines[index++] = i2; + + lines[index++] = i2; + lines[index] = i0; + } + + function trianglesToLines(triangles) { + var count = triangles.length; + var size = (count / 3) * 6; + var lines = IndexDatatype.createTypedArray(count, size); + + var index = 0; + for ( var i = 0; i < count; i += 3, index += 6) { + addTriangle(lines, index, triangles[i], triangles[i + 1], triangles[i + 2]); + } + + return lines; + } + + function triangleStripToLines(triangles) { + var count = triangles.length; + if (count >= 3) { + var size = (count - 2) * 6; + var lines = IndexDatatype.createTypedArray(count, size); + + addTriangle(lines, 0, triangles[0], triangles[1], triangles[2]); + var index = 6; + + for ( var i = 3; i < count; ++i, index += 6) { + addTriangle(lines, index, triangles[i - 1], triangles[i], triangles[i - 2]); + } + + return lines; + } + + return new Uint16Array(); + } + + function triangleFanToLines(triangles) { + if (triangles.length > 0) { + var count = triangles.length - 1; + var size = (count - 1) * 6; + var lines = IndexDatatype.createTypedArray(count, size); + + var base = triangles[0]; + var index = 0; + for ( var i = 1; i < count; ++i, index += 6) { + addTriangle(lines, index, base, triangles[i], triangles[i + 1]); + } + + return lines; + } + + return new Uint16Array(); + } + + /** + * Converts a geometry's triangle indices to line indices. If the geometry has an <code>indices</code> + * and its <code>primitiveType</code> is <code>TRIANGLES</code>, <code>TRIANGLE_STRIP</code>, + * <code>TRIANGLE_FAN</code>, it is converted to <code>LINES</code>; otherwise, the geometry is not changed. + * <p> + * This is commonly used to create a wireframe geometry for visual debugging. + * </p> + * + * @param {Geometry} geometry The geometry to modify. + * + * @returns {Geometry} The modified <code>geometry</code> argument, with its triangle indices converted to lines. + * + * @exception {DeveloperError} geometry is required. + * @exception {DeveloperError} geometry.primitiveType must be TRIANGLES, TRIANGLE_STRIP, or TRIANGLE_FAN. + * + * @example + * geometry = GeometryPipeline.toWireframe(geometry); + */ + GeometryPipeline.toWireframe = function(geometry) { + if (!defined(geometry)) { + throw new DeveloperError('geometry is required.'); + } + + var indices = geometry.indices; + if (defined(indices)) { + switch (geometry.primitiveType) { + case PrimitiveType.TRIANGLES: + geometry.indices = trianglesToLines(indices); + break; + case PrimitiveType.TRIANGLE_STRIP: + geometry.indices = triangleStripToLines(indices); + break; + case PrimitiveType.TRIANGLE_FAN: + geometry.indices = triangleFanToLines(indices); + break; + default: + throw new DeveloperError('geometry.primitiveType must be TRIANGLES, TRIANGLE_STRIP, or TRIANGLE_FAN.'); + } + + geometry.primitiveType = PrimitiveType.LINES; + } + + return geometry; + }; + + /** + * Creates a new {@link Geometry} with <code>LINES</code> representing the provided + * attribute (<code>attributeName</code>) for the provided geometry. This is used to + * visualize vector attributes like normals, binormals, and tangents. + * + * @param {Geometry} geometry The <code>Geometry</code> instance with the attribute. + * @param {String} [attributeName='normal'] The name of the attribute. + * @param {Number} [length=10000.0] The length of each line segment in meters. This can be negative to point the vector in the opposite direction. + * + * @returns {Geometry} A new <code>Geometry<code> instance with line segments for the vector. + * + * @exception {DeveloperError} geometry is required. + * @exception {DeveloperError} geometry.attributes.position is required. + * @exception {DeveloperError} geometry.attributes must have an attribute with the same name as the attributeName parameter. + * + * @example + * var geometry = GeometryPipeline.createLineSegmentsForVectors(instance.geometry, 'binormal', 100000.0), + */ + GeometryPipeline.createLineSegmentsForVectors = function(geometry, attributeName, length) { + if (!defined(geometry)) { + throw new DeveloperError('geometry is required.'); + } + + if (!defined(geometry.attributes.position)) { + throw new DeveloperError('geometry.attributes.position is required.'); + } + + attributeName = defaultValue(attributeName, 'normal'); + + if (!defined(geometry.attributes[attributeName])) { + throw new DeveloperError('geometry.attributes must have an attribute with the same name as the attributeName parameter, ' + attributeName + '.'); + } + + length = defaultValue(length, 10000.0); + + var positions = geometry.attributes.position.values; + var vectors = geometry.attributes[attributeName].values; + var positionsLength = positions.length; + + var newPositions = new Float64Array(2 * positionsLength); + + var j = 0; + for (var i = 0; i < positionsLength; i += 3) { + newPositions[j++] = positions[i]; + newPositions[j++] = positions[i + 1]; + newPositions[j++] = positions[i + 2]; + + newPositions[j++] = positions[i] + (vectors[i] * length); + newPositions[j++] = positions[i + 1] + (vectors[i + 1] * length); + newPositions[j++] = positions[i + 2] + (vectors[i + 2] * length); + } + + var newBoundingSphere; + var bs = geometry.boundingSphere; + if (defined(bs)) { + newBoundingSphere = new BoundingSphere(bs.center, bs.radius + length); + } + + return new Geometry({ + attributes : { + position : new GeometryAttribute({ + componentDatatype : ComponentDatatype.DOUBLE, + componentsPerAttribute : 3, + values : newPositions + }) + }, + primitiveType : PrimitiveType.LINES, + boundingSphere : newBoundingSphere + }); + }; + + /** + * Creates an object that maps attribute names to unique indices for matching + * vertex attributes and shader programs. + * + * @param {Geometry} geometry The geometry, which is not modified, to create the object for. + * + * @returns {Object} An object with attribute name / index pairs. + * + * @exception {DeveloperError} geometry is required. + * + * @example + * var attributeIndices = GeometryPipeline.createAttributeIndices(geometry); + * // Example output + * // { + * // 'position' : 0, + * // 'normal' : 1 + * // } + * + * @see Context#createVertexArrayFromGeometry + * @see ShaderCache + */ + GeometryPipeline.createAttributeIndices = function(geometry) { + if (!defined(geometry)) { + throw new DeveloperError('geometry is required.'); + } + + // There can be a WebGL performance hit when attribute 0 is disabled, so + // assign attribute locations to well-known attributes. + var semantics = [ + 'position', + 'positionHigh', + 'positionLow', + + // From VertexFormat.position - after 2D projection and high-precision encoding + 'position3DHigh', + 'position3DLow', + 'position2DHigh', + 'position2DLow', + + // From Primitive + 'pickColor', + + // From VertexFormat + 'normal', + 'st', + 'binormal', + 'tangent' + ]; + + var attributes = geometry.attributes; + var indices = {}; + var j = 0; + var i; + var len = semantics.length; + + // Attribute locations for well-known attributes + for (i = 0; i < len; ++i) { + var semantic = semantics[i]; + + if (defined(attributes[semantic])) { + indices[semantic] = j++; + } + } + + // Locations for custom attributes + for (var name in attributes) { + if (attributes.hasOwnProperty(name) && (!defined(indices[name]))) { + indices[name] = j++; + } + } + + return indices; + }; + + /** + * Reorders a geometry's attributes and <code>indices</code> to achieve better performance from the GPU's pre-vertex-shader cache. + * + * @param {Geometry} geometry The geometry to modify. + * + * @returns {Geometry} The modified <code>geometry</code> argument, with its attributes and indices reordered for the GPU's pre-vertex-shader cache. + * + * @exception {DeveloperError} geometry is required. + * @exception {DeveloperError} Each attribute array in geometry.attributes must have the same number of attributes. + * + * @example + * geometry = GeometryPipeline.reorderForPreVertexCache(geometry); + * + * @see GeometryPipeline.reorderForPostVertexCache + */ + GeometryPipeline.reorderForPreVertexCache = function(geometry) { + if (!defined(geometry)) { + throw new DeveloperError('geometry is required.'); + } + + var numVertices = Geometry.computeNumberOfVertices(geometry); + + var indices = geometry.indices; + if (defined(indices)) { + var indexCrossReferenceOldToNew = new Int32Array(numVertices); + for ( var i = 0; i < numVertices; i++) { + indexCrossReferenceOldToNew[i] = -1; + } + + // Construct cross reference and reorder indices + var indicesIn = indices; + var numIndices = indicesIn.length; + var indicesOut = IndexDatatype.createTypedArray(numVertices, numIndices); + + var intoIndicesIn = 0; + var intoIndicesOut = 0; + var nextIndex = 0; + var tempIndex; + while (intoIndicesIn < numIndices) { + tempIndex = indexCrossReferenceOldToNew[indicesIn[intoIndicesIn]]; + if (tempIndex !== -1) { + indicesOut[intoIndicesOut] = tempIndex; + } else { + tempIndex = indicesIn[intoIndicesIn]; + indexCrossReferenceOldToNew[tempIndex] = nextIndex; + + indicesOut[intoIndicesOut] = nextIndex; + ++nextIndex; + } + ++intoIndicesIn; + ++intoIndicesOut; + } + geometry.indices = indicesOut; + + // Reorder attributes + var attributes = geometry.attributes; + for ( var property in attributes) { + if (attributes.hasOwnProperty(property) && + defined(attributes[property]) && + defined(attributes[property].values)) { + + var attribute = attributes[property]; + var elementsIn = attribute.values; + var intoElementsIn = 0; + var numComponents = attribute.componentsPerAttribute; + var elementsOut = ComponentDatatype.createTypedArray(attribute.componentDatatype, nextIndex * numComponents); + while (intoElementsIn < numVertices) { + var temp = indexCrossReferenceOldToNew[intoElementsIn]; + if (temp !== -1) { + for (i = 0; i < numComponents; i++) { + elementsOut[numComponents * temp + i] = elementsIn[numComponents * intoElementsIn + i]; + } + } + ++intoElementsIn; + } + attribute.values = elementsOut; + } + } + } + + return geometry; + }; + + /** + * Reorders a geometry's <code>indices</code> to achieve better performance from the GPU's + * post vertex-shader cache by using the Tipsify algorithm. If the geometry <code>primitiveType</code> + * is not <code>TRIANGLES</code> or the geometry does not have an <code>indices</code>, this function has no effect. + * + * @param {Geometry} geometry The geometry to modify. + * @param {Number} [cacheCapacity=24] The number of vertices that can be held in the GPU's vertex cache. + * + * @returns {Geometry} The modified <code>geometry</code> argument, with its indices reordered for the post-vertex-shader cache. + * + * @exception {DeveloperError} geometry is required. + * @exception {DeveloperError} cacheCapacity must be greater than two. + * + * @example + * geometry = GeometryPipeline.reorderForPostVertexCache(geometry); + * + * @see GeometryPipeline.reorderForPreVertexCache + * @see <a href='http://gfx.cs.princeton.edu/pubs/Sander_2007_%3ETR/tipsy.pdf'> + * Fast Triangle Reordering for Vertex Locality and Reduced Overdraw</a> + * by Sander, Nehab, and Barczak + */ + GeometryPipeline.reorderForPostVertexCache = function(geometry, cacheCapacity) { + if (!defined(geometry)) { + throw new DeveloperError('geometry is required.'); + } + + var indices = geometry.indices; + if ((geometry.primitiveType === PrimitiveType.TRIANGLES) && (defined(indices))) { + var numIndices = indices.length; + var maximumIndex = 0; + for ( var j = 0; j < numIndices; j++) { + if (indices[j] > maximumIndex) { + maximumIndex = indices[j]; + } + } + geometry.indices = Tipsify.tipsify({ + indices : indices, + maximumIndex : maximumIndex, + cacheSize : cacheCapacity + }); + } + + return geometry; + }; + + function copyAttributesDescriptions(attributes) { + var newAttributes = {}; + + for ( var attribute in attributes) { + if (attributes.hasOwnProperty(attribute) && + defined(attributes[attribute]) && + defined(attributes[attribute].values)) { + + var attr = attributes[attribute]; + newAttributes[attribute] = new GeometryAttribute({ + componentDatatype : attr.componentDatatype, + componentsPerAttribute : attr.componentsPerAttribute, + normalize : attr.normalize, + values : [] + }); + } + } + + return newAttributes; + } + + function copyVertex(destinationAttributes, sourceAttributes, index) { + for ( var attribute in sourceAttributes) { + if (sourceAttributes.hasOwnProperty(attribute) && + defined(sourceAttributes[attribute]) && + defined(sourceAttributes[attribute].values)) { + + var attr = sourceAttributes[attribute]; + + for ( var k = 0; k < attr.componentsPerAttribute; ++k) { + destinationAttributes[attribute].values.push(attr.values[(index * attr.componentsPerAttribute) + k]); + } + } + } + } + + /** + * Splits a geometry into multiple geometries, if necessary, to ensure that indices in the + * <code>indices</code> fit into unsigned shorts. This is used to meet the WebGL requirements + * when {@link Context#getElementIndexUint} is <code>false</code>. + * <p> + * If the geometry does not have any <code>indices</code>, this function has no effect. + * </p> + * + * @param {Geometry} geometry The geometry to be split into multiple geometries. + * + * @returns {Array} An array of geometries, each with indices that fit into unsigned shorts. + * + * @exception {DeveloperError} geometry is required. + * @exception {DeveloperError} geometry.primitiveType must equal to PrimitiveType.TRIANGLES, PrimitiveType.LINES, or PrimitiveType.POINTS + * @exception {DeveloperError} All geometry attribute lists must have the same number of attributes. + * + * @example + * var geometries = GeometryPipeline.fitToUnsignedShortIndices(geometry); + */ + GeometryPipeline.fitToUnsignedShortIndices = function(geometry) { + if (!defined(geometry)) { + throw new DeveloperError('geometry is required.'); + } + + if ((defined(geometry.indices)) && + ((geometry.primitiveType !== PrimitiveType.TRIANGLES) && + (geometry.primitiveType !== PrimitiveType.LINES) && + (geometry.primitiveType !== PrimitiveType.POINTS))) { + throw new DeveloperError('geometry.primitiveType must equal to PrimitiveType.TRIANGLES, PrimitiveType.LINES, or PrimitiveType.POINTS.'); + } + + var geometries = []; + + // If there's an index list and more than 64K attributes, it is possible that + // some indices are outside the range of unsigned short [0, 64K - 1] + var numberOfVertices = Geometry.computeNumberOfVertices(geometry); + if (defined(geometry.indices) && (numberOfVertices > CesiumMath.SIXTY_FOUR_KILOBYTES)) { + var oldToNewIndex = []; + var newIndices = []; + var currentIndex = 0; + var newAttributes = copyAttributesDescriptions(geometry.attributes); + + var originalIndices = geometry.indices; + var numberOfIndices = originalIndices.length; + + var indicesPerPrimitive; + + if (geometry.primitiveType === PrimitiveType.TRIANGLES) { + indicesPerPrimitive = 3; + } else if (geometry.primitiveType === PrimitiveType.LINES) { + indicesPerPrimitive = 2; + } else if (geometry.primitiveType === PrimitiveType.POINTS) { + indicesPerPrimitive = 1; + } + + for ( var j = 0; j < numberOfIndices; j += indicesPerPrimitive) { + for (var k = 0; k < indicesPerPrimitive; ++k) { + var x = originalIndices[j + k]; + var i = oldToNewIndex[x]; + if (!defined(i)) { + i = currentIndex++; + oldToNewIndex[x] = i; + copyVertex(newAttributes, geometry.attributes, x); + } + newIndices.push(i); + } + + if (currentIndex + indicesPerPrimitive > CesiumMath.SIXTY_FOUR_KILOBYTES) { + geometries.push(new Geometry({ + attributes : newAttributes, + indices : newIndices, + primitiveType : geometry.primitiveType, + boundingSphere : geometry.boundingSphere + })); + + // Reset for next vertex-array + oldToNewIndex = []; + newIndices = []; + currentIndex = 0; + newAttributes = copyAttributesDescriptions(geometry.attributes); + } + } + + if (newIndices.length !== 0) { + geometries.push(new Geometry({ + attributes : newAttributes, + indices : newIndices, + primitiveType : geometry.primitiveType, + boundingSphere : geometry.boundingSphere + })); + } + } else { + // No need to split into multiple geometries + geometries.push(geometry); + } + + return geometries; + }; + + var scratchProjectTo2DCartesian3 = new Cartesian3(); + var scratchProjectTo2DCartographic = new Cartographic(); + + /** + * Projects a geometry's 3D <code>position</code> attribute to 2D, replacing the <code>position</code> + * attribute with separate <code>position3D</code> and <code>position2D</code> attributes. + * <p> + * If the geometry does not have a <code>position</code>, this function has no effect. + * </p> + * + * @param {Geometry} geometry The geometry to modify. + * @param {String} attributeName The name of the attribute. + * @param {String} attributeName3D The name of the attribute in 3D. + * @param {String} attributeName2D The name of the attribute in 2D. + * @param {Object} [projection=new GeographicProjection()] The projection to use. + * + * @returns {Geometry} The modified <code>geometry</code> argument with <code>position3D</code> and <code>position2D</code> attributes. + * + * @exception {DeveloperError} geometry is required. + * @exception {DeveloperError} attributeName is required. + * @exception {DeveloperError} attributeName3D is required. + * @exception {DeveloperError} attributeName2D is required. + * @exception {DeveloperError} geometry must have attribute matching the attributeName argument. + * @exception {DeveloperError} The attribute componentDatatype must be ComponentDatatype.DOUBLE. + * @exception {DeveloperError} Could not project a point to 2D. + * + * @example + * geometry = GeometryPipeline.projectTo2D(geometry, 'position', 'position3D', 'position2D'); + */ + GeometryPipeline.projectTo2D = function(geometry, attributeName, attributeName3D, attributeName2D, projection) { + if (!defined(geometry)) { + throw new DeveloperError('geometry is required.'); + } + + if (!defined(attributeName)) { + throw new DeveloperError('attributeName is required.'); + } + + if (!defined(attributeName3D)) { + throw new DeveloperError('attributeName3D is required.'); + } + + if (!defined(attributeName2D)) { + throw new DeveloperError('attributeName2D is required.'); + } + + var attribute = geometry.attributes[attributeName]; + if (!defined(attribute)) { + throw new DeveloperError('geometry must have attribute matching the attributeName argument: ' + attributeName + '.'); + } + + if (attribute.componentDatatype.value !== ComponentDatatype.DOUBLE.value) { + throw new DeveloperError('The attribute componentDatatype must be ComponentDatatype.DOUBLE.'); + } + + projection = (defined(projection)) ? projection : new GeographicProjection(); + var ellipsoid = projection.getEllipsoid(); + + // Project original values to 2D. + var values3D = attribute.values; + var projectedValues = new Float64Array(values3D.length); + var index = 0; + + for ( var i = 0; i < values3D.length; i += 3) { + var value = Cartesian3.fromArray(values3D, i, scratchProjectTo2DCartesian3); + + var lonLat = ellipsoid.cartesianToCartographic(value, scratchProjectTo2DCartographic); + if (!defined(lonLat)) { + throw new DeveloperError('Could not project point (' + value.x + ', ' + value.y + ', ' + value.z + ') to 2D.'); + } + + var projectedLonLat = projection.project(lonLat, scratchProjectTo2DCartesian3); + + projectedValues[index++] = projectedLonLat.x; + projectedValues[index++] = projectedLonLat.y; + projectedValues[index++] = projectedLonLat.z; + } + + // Rename original cartesians to WGS84 cartesians. + geometry.attributes[attributeName3D] = attribute; + + // Replace original cartesians with 2D projected cartesians + geometry.attributes[attributeName2D] = new GeometryAttribute({ + componentDatatype : ComponentDatatype.DOUBLE, + componentsPerAttribute : 3, + values : projectedValues + }); + delete geometry.attributes[attributeName]; + + return geometry; + }; + + var encodedResult = { + high : 0.0, + low : 0.0 + }; + + /** + * Encodes floating-point geometry attribute values as two separate attributes to improve + * rendering precision using the same encoding as {@link EncodedCartesian3}. + * <p> + * This is commonly used to create high-precision position vertex attributes. + * </p> + * + * @param {Geometry} geometry The geometry to modify. + * @param {String} attributeName The name of the attribute. + * @param {String} attributeHighName The name of the attribute for the encoded high bits. + * @param {String} attributeLowName The name of the attribute for the encoded low bits. + * + * @returns {Geometry} The modified <code>geometry</code> argument, with its encoded attribute. + * + * @exception {DeveloperError} geometry is required. + * @exception {DeveloperError} attributeName is required. + * @exception {DeveloperError} attributeHighName is required. + * @exception {DeveloperError} attributeLowName is required. + * @exception {DeveloperError} geometry must have attribute matching the attributeName argument. + * @exception {DeveloperError} The attribute componentDatatype must be ComponentDatatype.DOUBLE. + * + * @example + * geometry = GeometryPipeline.encodeAttribute(geometry, 'position3D', 'position3DHigh', 'position3DLow'); + * + * @see EncodedCartesian3 + */ + GeometryPipeline.encodeAttribute = function(geometry, attributeName, attributeHighName, attributeLowName) { + if (!defined(geometry)) { + throw new DeveloperError('geometry is required.'); + } + + if (!defined(attributeName)) { + throw new DeveloperError('attributeName is required.'); + } + + if (!defined(attributeHighName)) { + throw new DeveloperError('attributeHighName is required.'); + } + + if (!defined(attributeLowName)) { + throw new DeveloperError('attributeLowName is required.'); + } + + var attribute = geometry.attributes[attributeName]; + + if (!defined(attribute)) { + throw new DeveloperError('geometry must have attribute matching the attributeName argument: ' + attributeName + '.'); + } + + if (attribute.componentDatatype.value !== ComponentDatatype.DOUBLE.value) { + throw new DeveloperError('The attribute componentDatatype must be ComponentDatatype.DOUBLE.'); + } + + var values = attribute.values; + var length = values.length; + var highValues = new Float32Array(length); + var lowValues = new Float32Array(length); + + for (var i = 0; i < length; ++i) { + EncodedCartesian3.encode(values[i], encodedResult); + highValues[i] = encodedResult.high; + lowValues[i] = encodedResult.low; + } + + var componentsPerAttribute = attribute.componentsPerAttribute; + + geometry.attributes[attributeHighName] = new GeometryAttribute({ + componentDatatype : ComponentDatatype.FLOAT, + componentsPerAttribute : componentsPerAttribute, + values : highValues + }); + geometry.attributes[attributeLowName] = new GeometryAttribute({ + componentDatatype : ComponentDatatype.FLOAT, + componentsPerAttribute : componentsPerAttribute, + values : lowValues + }); + delete geometry.attributes[attributeName]; + + return geometry; + }; + + var scratchCartesian3 = new Cartesian3(); + + function transformPoint(matrix, attribute) { + if (defined(attribute)) { + var values = attribute.values; + var length = values.length; + for (var i = 0; i < length; i += 3) { + Cartesian3.unpack(values, i, scratchCartesian3); + Matrix4.multiplyByPoint(matrix, scratchCartesian3, scratchCartesian3); + Cartesian3.pack(scratchCartesian3, values, i); + } + } + } + + function transformVector(matrix, attribute) { + if (defined(attribute)) { + var values = attribute.values; + var length = values.length; + for (var i = 0; i < length; i += 3) { + Cartesian3.unpack(values, i, scratchCartesian3); + Matrix3.multiplyByVector(matrix, scratchCartesian3, scratchCartesian3); + scratchCartesian3 = Cartesian3.normalize(scratchCartesian3, scratchCartesian3); + Cartesian3.pack(scratchCartesian3, values, i); + } + } + } + + var inverseTranspose = new Matrix4(); + var normalMatrix = new Matrix3(); + + /** + * Transforms a geometry instance to world coordinates. This is used as a prerequisite + * to batch together several instances with {@link GeometryPipeline.combine}. This changes + * the instance's <code>modelMatrix</code> to {@see Matrix4.IDENTITY} and transforms the + * following attributes if they are present: <code>position</code>, <code>normal</code>, + * <code>binormal</code>, and <code>tangent</code>. + * + * @param {GeometryInstance} instance The geometry instance to modify. + * + * @returns {GeometryInstance} The modified <code>instance</code> argument, with its attributes transforms to world coordinates. + * + * @exception {DeveloperError} instance is required. + * + * @example + * for (var i = 0; i < instances.length; ++i) { + * GeometryPipeline.transformToWorldCoordinates(instances[i]); + * } + * var geometry = GeometryPipeline.combine(instances); + * + * @see GeometryPipeline.combine + */ + GeometryPipeline.transformToWorldCoordinates = function(instance) { + if (!defined(instance)) { + throw new DeveloperError('instance is required.'); + } + + var modelMatrix = instance.modelMatrix; + + if (Matrix4.equals(modelMatrix, Matrix4.IDENTITY)) { + // Already in world coordinates + return instance; + } + + var attributes = instance.geometry.attributes; + + // Transform attributes in known vertex formats + transformPoint(modelMatrix, attributes.position); + transformPoint(modelMatrix, attributes.prevPosition); + transformPoint(modelMatrix, attributes.nextPosition); + + if ((defined(attributes.normal)) || + (defined(attributes.binormal)) || + (defined(attributes.tangent))) { + + Matrix4.inverse(modelMatrix, inverseTranspose); + Matrix4.transpose(inverseTranspose, inverseTranspose); + Matrix4.getRotation(inverseTranspose, normalMatrix); + + transformVector(normalMatrix, attributes.normal); + transformVector(normalMatrix, attributes.binormal); + transformVector(normalMatrix, attributes.tangent); + } + + var boundingSphere = instance.geometry.boundingSphere; + + if (defined(boundingSphere)) { + instance.geometry.boundingSphere = BoundingSphere.transform(boundingSphere, modelMatrix, boundingSphere); + } + + instance.modelMatrix = Matrix4.clone(Matrix4.IDENTITY); + + return instance; + }; + + function findAttributesInAllGeometries(instances) { + var length = instances.length; + + var attributesInAllGeometries = {}; + + var attributes0 = instances[0].geometry.attributes; + var name; + + for (name in attributes0) { + if (attributes0.hasOwnProperty(name) && + defined(attributes0[name]) && + defined(attributes0[name].values)) { + + var attribute = attributes0[name]; + var numberOfComponents = attribute.values.length; + var inAllGeometries = true; + + // Does this same attribute exist in all geometries? + for (var i = 1; i < length; ++i) { + var otherAttribute = instances[i].geometry.attributes[name]; + + if ((!defined(otherAttribute)) || + (attribute.componentDatatype.value !== otherAttribute.componentDatatype.value) || + (attribute.componentsPerAttribute !== otherAttribute.componentsPerAttribute) || + (attribute.normalize !== otherAttribute.normalize)) { + + inAllGeometries = false; + break; + } + + numberOfComponents += otherAttribute.values.length; + } + + if (inAllGeometries) { + attributesInAllGeometries[name] = new GeometryAttribute({ + componentDatatype : attribute.componentDatatype, + componentsPerAttribute : attribute.componentsPerAttribute, + normalize : attribute.normalize, + values : ComponentDatatype.createTypedArray(attribute.componentDatatype, numberOfComponents) + }); + } + } + } + + return attributesInAllGeometries; + } + + /** + * Combines geometry from several {@link GeometryInstance} objects into one geometry. + * This concatenates the attributes, concatenates and adjusts the indices, and creates + * a bounding sphere encompassing all instances. + * <p> + * If the instances do not have the same attributes, a subset of attributes common + * to all instances is used, and the others are ignored. + * </p> + * <p> + * This is used by {@link Primitive} to efficiently render a large amount of static data. + * </p> + * + * @param {Array} [instances] The array of {@link GeometryInstance} objects whose geometry will be combined. + * + * @returns {Geometry} A single geometry created from the provided geometry instances. + * + * @exception {DeveloperError} instances is required and must have length greater than zero. + * @exception {DeveloperError} All instances must have the same modelMatrix. + * @exception {DeveloperError} All instance geometries must have an indices or not have one. + * @exception {DeveloperError} All instance geometries must have the same primitiveType. + * + * @example + * for (var i = 0; i < instances.length; ++i) { + * GeometryPipeline.transformToWorldCoordinates(instances[i]); + * } + * var geometry = GeometryPipeline.combine(instances); + * + * @see GeometryPipeline.transformToWorldCoordinates + */ + GeometryPipeline.combine = function(instances) { + if ((!defined(instances)) || (instances.length < 1)) { + throw new DeveloperError('instances is required and must have length greater than zero.'); + } + + var length = instances.length; + + var name; + var i; + var j; + var k; + + var m = instances[0].modelMatrix; + var haveIndices = (defined(instances[0].geometry.indices)); + var primitiveType = instances[0].geometry.primitiveType; + + for (i = 1; i < length; ++i) { + if (!Matrix4.equals(instances[i].modelMatrix, m)) { + throw new DeveloperError('All instances must have the same modelMatrix.'); + } + + if ((defined(instances[i].geometry.indices)) !== haveIndices) { + throw new DeveloperError('All instance geometries must have an indices or not have one.'); + } + + if (instances[i].geometry.primitiveType !== primitiveType) { + throw new DeveloperError('All instance geometries must have the same primitiveType.'); + } + } + + // Find subset of attributes in all geometries + var attributes = findAttributesInAllGeometries(instances); + var values; + var sourceValues; + var sourceValuesLength; + + // Combine attributes from each geometry into a single typed array + for (name in attributes) { + if (attributes.hasOwnProperty(name)) { + values = attributes[name].values; + + k = 0; + for (i = 0; i < length; ++i) { + sourceValues = instances[i].geometry.attributes[name].values; + sourceValuesLength = sourceValues.length; + + for (j = 0; j < sourceValuesLength; ++j) { + values[k++] = sourceValues[j]; + } + } + } + } + + // Combine index lists + var indices; + + if (haveIndices) { + var numberOfIndices = 0; + for (i = 0; i < length; ++i) { + numberOfIndices += instances[i].geometry.indices.length; + } + + var numberOfVertices = Geometry.computeNumberOfVertices(new Geometry({ + attributes : attributes, + primitiveType : PrimitiveType.POINTS + })); + var destIndices = IndexDatatype.createTypedArray(numberOfVertices, numberOfIndices); + + var destOffset = 0; + var offset = 0; + + for (i = 0; i < length; ++i) { + var sourceIndices = instances[i].geometry.indices; + var sourceIndicesLen = sourceIndices.length; + + for (k = 0; k < sourceIndicesLen; ++k) { + destIndices[destOffset++] = offset + sourceIndices[k]; + } + + offset += Geometry.computeNumberOfVertices(instances[i].geometry); + } + + indices = destIndices; + } + + // Create bounding sphere that includes all instances + var center = new Cartesian3(); + var radius = 0.0; + var bs; + + for (i = 0; i < length; ++i) { + bs = instances[i].geometry.boundingSphere; + if (!defined(bs)) { + // If any geometries have an undefined bounding sphere, then so does the combined geometry + center = undefined; + break; + } + + Cartesian3.add(bs.center, center, center); + } + + if (defined(center)) { + Cartesian3.divideByScalar(center, length, center); + + for (i = 0; i < length; ++i) { + bs = instances[i].geometry.boundingSphere; + var tempRadius = Cartesian3.magnitude(Cartesian3.subtract(bs.center, center)) + bs.radius; + + if (tempRadius > radius) { + radius = tempRadius; + } + } + } + + return new Geometry({ + attributes : attributes, + indices : indices, + primitiveType : primitiveType, + boundingSphere : (defined(center)) ? new BoundingSphere(center, radius) : undefined + }); + }; + + var normal = new Cartesian3(); + var v0 = new Cartesian3(); + var v1 = new Cartesian3(); + var v2 = new Cartesian3(); + + /** + * Computes per-vertex normals for a geometry containing <code>TRIANGLES</code> by averaging the normals of + * all triangles incident to the vertex. The result is a new <code>normal</code> attribute added to the geometry. + * This assumes a counter-clockwise winding order. + * + * @param {Geometry} geometry The geometry to modify. + * + * @returns {Geometry} The modified <code>geometry</code> argument with the computed <code>normal</code> attribute. + * + * @exception {DeveloperError} geometry is required. + * @exception {DeveloperError} geometry.attributes.position.values is required. + * @exception {DeveloperError} geometry.indices is required. + * @exception {DeveloperError} geometry.indices length must be greater than 0 and be a multiple of 3. + * @exception {DeveloperError} geometry.primitiveType must be {@link PrimitiveType.TRIANGLES}. + * + * @example + * GeometryPipeline.computeNormal(geometry); + */ + GeometryPipeline.computeNormal = function(geometry) { + if (!defined(geometry)) { + throw new DeveloperError('geometry is required.'); + } + + var attributes = geometry.attributes; + var indices = geometry.indices; + + if (!defined(attributes.position) || !defined(attributes.position.values)) { + throw new DeveloperError('geometry.attributes.position.values is required.'); + } + + if (!defined(indices)) { + throw new DeveloperError('geometry.indices is required.'); + } + + if (indices.length < 2 || indices.length % 3 !== 0) { + throw new DeveloperError('geometry.indices length must be greater than 0 and be a multiple of 3.'); + } + + if (geometry.primitiveType !== PrimitiveType.TRIANGLES) { + throw new DeveloperError('geometry.primitiveType must be PrimitiveType.TRIANGLES.'); + } + + var vertices = geometry.attributes.position.values; + var numVertices = geometry.attributes.position.values.length / 3; + var numIndices = indices.length; + var normalsPerVertex = new Array(numVertices); + var normalsPerTriangle = new Array(numIndices / 3); + var normalIndices = new Array(numIndices); + + for ( var i = 0; i < numVertices; i++) { + normalsPerVertex[i] = { + indexOffset : 0, + count : 0, + currentCount : 0 + }; + } + + var j = 0; + for (i = 0; i < numIndices; i += 3) { + var i0 = indices[i]; + var i1 = indices[i + 1]; + var i2 = indices[i + 2]; + var i03 = i0 * 3; + var i13 = i1 * 3; + var i23 = i2 * 3; + + v0.x = vertices[i03]; + v0.y = vertices[i03 + 1]; + v0.z = vertices[i03 + 2]; + v1.x = vertices[i13]; + v1.y = vertices[i13 + 1]; + v1.z = vertices[i13 + 2]; + v2.x = vertices[i23]; + v2.y = vertices[i23 + 1]; + v2.z = vertices[i23 + 2]; + + normalsPerVertex[i0].count++; + normalsPerVertex[i1].count++; + normalsPerVertex[i2].count++; + + Cartesian3.subtract(v1, v0, v1); + Cartesian3.subtract(v2, v0, v2); + normalsPerTriangle[j] = Cartesian3.cross(v1, v2); + j++; + } + + var indexOffset = 0; + for (i = 0; i < numVertices; i++) { + normalsPerVertex[i].indexOffset += indexOffset; + indexOffset += normalsPerVertex[i].count; + } + + j = 0; + var vertexNormalData; + for (i = 0; i < numIndices; i += 3) { + vertexNormalData = normalsPerVertex[indices[i]]; + var index = vertexNormalData.indexOffset + vertexNormalData.currentCount; + normalIndices[index] = j; + vertexNormalData.currentCount++; + + vertexNormalData = normalsPerVertex[indices[i + 1]]; + index = vertexNormalData.indexOffset + vertexNormalData.currentCount; + normalIndices[index] = j; + vertexNormalData.currentCount++; + + vertexNormalData = normalsPerVertex[indices[i + 2]]; + index = vertexNormalData.indexOffset + vertexNormalData.currentCount; + normalIndices[index] = j; + vertexNormalData.currentCount++; + + j++; + } + + var normalValues = new Float32Array(numVertices * 3); + for (i = 0; i < numVertices; i++) { + var i3 = i * 3; + vertexNormalData = normalsPerVertex[i]; + if (vertexNormalData.count > 0) { + Cartesian3.clone(Cartesian3.ZERO, normal); + for (j = 0; j < vertexNormalData.count; j++) { + Cartesian3.add(normal, normalsPerTriangle[normalIndices[vertexNormalData.indexOffset + j]], normal); + } + Cartesian3.normalize(normal, normal); + normalValues[i3] = normal.x; + normalValues[i3 + 1] = normal.y; + normalValues[i3 + 2] = normal.z; + } else { + normalValues[i3] = 0.0; + normalValues[i3 + 1] = 0.0; + normalValues[i3 + 2] = 1.0; + } + } + + geometry.attributes.normal = new GeometryAttribute({ + componentDatatype : ComponentDatatype.FLOAT, + componentsPerAttribute : 3, + values : normalValues + }); + + return geometry; + }; + + var normalScratch = new Cartesian3(); + var normalScale = new Cartesian3(); + var tScratch = new Cartesian3(); + + /** + * Computes per-vertex binormals and tangents for a geometry containing <code>TRIANGLES</code>. + * The result is new <code>binormal</code> and <code>tangent</code> attributes added to the geometry. + * This assumes a counter-clockwise winding order. + * <p> + * Based on <a href="http://www.terathon.com/code/tangent.html">Computing Tangent Space Basis Vectors + * for an Arbitrary Mesh</a> by Eric Lengyel. + * </p> + * + * @param {Geometry} geometry The geometry to modify. + * + * @returns {Geometry} The modified <code>geometry</code> argument with the computed <code>binormal</code> and <code>tangent</code> attributes. + * + * @exception {DeveloperError} geometry is required. + * @exception {DeveloperError} geometry.attributes.position.values is required. + * @exception {DeveloperError} geometry.attributes.normal.values is required. + * @exception {DeveloperError} geometry.attributes.st.values is required. + * @exception {DeveloperError} geometry.indices is required. + * @exception {DeveloperError} geometry.indices length must be greater than 0 and be a multiple of 3. + * @exception {DeveloperError} geometry.primitiveType must be {@link PrimitiveType.TRIANGLES}. + * + * @example + * GeometryPipeline.computeBinormalAndTangent(geometry); + */ + GeometryPipeline.computeBinormalAndTangent = function(geometry) { + if (!defined(geometry)) { + throw new DeveloperError('geometry is required.'); + } + + var attributes = geometry.attributes; + var indices = geometry.indices; + + if (!defined(attributes.position) || !defined(attributes.position.values)) { + throw new DeveloperError('geometry.attributes.position.values is required.'); + } + + if (!defined(attributes.normal) || !defined(attributes.normal.values)) { + throw new DeveloperError('geometry.attributes.normal.values is required.'); + } + + if (!defined(attributes.st) || !defined(attributes.st.values)) { + throw new DeveloperError('geometry.attributes.st.values is required.'); + } + + if (!defined(indices)) { + throw new DeveloperError('geometry.indices is required.'); + } + + if (indices.length < 2 || indices.length % 3 !== 0) { + throw new DeveloperError('geometry.indices length must be greater than 0 and be a multiple of 3.'); + } + + if (geometry.primitiveType !== PrimitiveType.TRIANGLES) { + throw new DeveloperError('geometry.primitiveType must be PrimitiveType.TRIANGLES.'); + } + + var vertices = geometry.attributes.position.values; + var normals = geometry.attributes.normal.values; + var st = geometry.attributes.st.values; + + var numVertices = geometry.attributes.position.values.length / 3; + var numIndices = indices.length; + var tan1 = new Array(numVertices * 3); + + for ( var i = 0; i < tan1.length; i++) { + tan1[i] = 0; + } + + var i03; + var i13; + var i23; + for (i = 0; i < numIndices; i += 3) { + var i0 = indices[i]; + var i1 = indices[i + 1]; + var i2 = indices[i + 2]; + i03 = i0 * 3; + i13 = i1 * 3; + i23 = i2 * 3; + var i02 = i0 * 2; + var i12 = i1 * 2; + var i22 = i2 * 2; + + var ux = vertices[i03]; + var uy = vertices[i03 + 1]; + var uz = vertices[i03 + 2]; + + var wx = st[i02]; + var wy = st[i02 + 1]; + var t1 = st[i12 + 1] - wy; + var t2 = st[i22 + 1] - wy; + + var r = 1.0 / ((st[i12] - wx) * t2 - (st[i22] - wx) * t1); + var sdirx = (t2 * (vertices[i13] - ux) - t1 * (vertices[i23] - ux)) * r; + var sdiry = (t2 * (vertices[i13 + 1] - uy) - t1 * (vertices[i23 + 1] - uy)) * r; + var sdirz = (t2 * (vertices[i13 + 2] - uz) - t1 * (vertices[i23 + 2] - uz)) * r; + + tan1[i03] += sdirx; + tan1[i03 + 1] += sdiry; + tan1[i03 + 2] += sdirz; + + tan1[i13] += sdirx; + tan1[i13 + 1] += sdiry; + tan1[i13 + 2] += sdirz; + + tan1[i23] += sdirx; + tan1[i23 + 1] += sdiry; + tan1[i23 + 2] += sdirz; + } + + var binormalValues = new Float32Array(numVertices * 3); + var tangentValues = new Float32Array(numVertices * 3); + + for (i = 0; i < numVertices; i++) { + i03 = i * 3; + i13 = i03 + 1; + i23 = i03 + 2; + + var n = Cartesian3.fromArray(normals, i03, normalScratch); + var t = Cartesian3.fromArray(tan1, i03, tScratch); + var scalar = Cartesian3.dot(n, t); + Cartesian3.multiplyByScalar(n, scalar, normalScale); + Cartesian3.normalize(Cartesian3.subtract(t, normalScale, t), t); + + tangentValues[i03] = t.x; + tangentValues[i13] = t.y; + tangentValues[i23] = t.z; + + Cartesian3.normalize(Cartesian3.cross(n, t, t), t); + + binormalValues[i03] = t.x; + binormalValues[i13] = t.y; + binormalValues[i23] = t.z; + } + + geometry.attributes.tangent = new GeometryAttribute({ + componentDatatype : ComponentDatatype.FLOAT, + componentsPerAttribute : 3, + values : tangentValues + }); + + geometry.attributes.binormal = new GeometryAttribute({ + componentDatatype : ComponentDatatype.FLOAT, + componentsPerAttribute : 3, + values : binormalValues + }); + + return geometry; + }; + + function indexTriangles(geometry) { + if (defined(geometry.indices)) { + return geometry; + } + + var numberOfVertices = Geometry.computeNumberOfVertices(geometry); + if (numberOfVertices < 3) { + throw new DeveloperError('The number of vertices must be at least three.'); + } + + if (numberOfVertices % 3 !== 0) { + throw new DeveloperError('The number of vertices must be a multiple of three.'); + } + + var indices = IndexDatatype.createTypedArray(numberOfVertices, numberOfVertices); + for (var i = 0; i < numberOfVertices; ++i) { + indices[i] = i; + } + + geometry.indices = indices; + return geometry; + } + + function indexTriangleFan(geometry) { + var numberOfVertices = Geometry.computeNumberOfVertices(geometry); + if (numberOfVertices < 3) { + throw new DeveloperError('The number of vertices must be at least three.'); + } + + var indices = IndexDatatype.createTypedArray(numberOfVertices, (numberOfVertices - 2) * 3); + indices[0] = 1; + indices[1] = 0; + indices[2] = 2; + + var indicesIndex = 3; + for (var i = 3; i < numberOfVertices; ++i) { + indices[indicesIndex++] = i - 1; + indices[indicesIndex++] = 0; + indices[indicesIndex++] = i; + } + + geometry.indices = indices; + geometry.primitiveType = PrimitiveType.TRIANGLES; + return geometry; + } + + function indexTriangleStrip(geometry) { + var numberOfVertices = Geometry.computeNumberOfVertices(geometry); + if (numberOfVertices < 3) { + throw new DeveloperError('The number of vertices must be at least 3.'); + } + + var indices = IndexDatatype.createTypedArray(numberOfVertices, (numberOfVertices - 2) * 3); + indices[0] = 0; + indices[1] = 1; + indices[2] = 2; + + if (numberOfVertices > 3) { + indices[3] = 0; + indices[4] = 2; + indices[5] = 3; + } + + var indicesIndex = 6; + for (var i = 3; i < numberOfVertices - 1; i += 2) { + indices[indicesIndex++] = i; + indices[indicesIndex++] = i - 1; + indices[indicesIndex++] = i + 1; + + if (i + 2 < numberOfVertices) { + indices[indicesIndex++] = i; + indices[indicesIndex++] = i + 1; + indices[indicesIndex++] = i + 2; + } + } + + geometry.indices = indices; + geometry.primitiveType = PrimitiveType.TRIANGLES; + return geometry; + } + + function indexLines(geometry) { + if (defined(geometry.indices)) { + return geometry; + } + + var numberOfVertices = Geometry.computeNumberOfVertices(geometry); + if (numberOfVertices < 2) { + throw new DeveloperError('The number of vertices must be at least two.'); + } + + if (numberOfVertices % 2 !== 0) { + throw new DeveloperError('The number of vertices must be a multiple of 2.'); + } + + var indices = IndexDatatype.createTypedArray(numberOfVertices, numberOfVertices); + for (var i = 0; i < numberOfVertices; ++i) { + indices[i] = i; + } + + geometry.indices = indices; + return geometry; + } + + function indexLineStrip(geometry) { + var numberOfVertices = Geometry.computeNumberOfVertices(geometry); + if (numberOfVertices < 2) { + throw new DeveloperError('The number of vertices must be at least two.'); + } + var indices = IndexDatatype.createTypedArray(numberOfVertices, (numberOfVertices - 1) * 2); + + indices[0] = 0; + indices[1] = 1; + + var indicesIndex = 2; + for (var i = 2; i < numberOfVertices; ++i) { + indices[indicesIndex++] = i - 1; + indices[indicesIndex++] = i; + } + + geometry.indices = indices; + geometry.primitiveType = PrimitiveType.LINES; + return geometry; + } + + function indexLineLoop(geometry) { + var numberOfVertices = Geometry.computeNumberOfVertices(geometry); + if (numberOfVertices < 2) { + throw new DeveloperError('The number of vertices must be at least two.'); + } + + var indices = IndexDatatype.createTypedArray(numberOfVertices, numberOfVertices * 2); + + indices[0] = 0; + indices[1] = 1; + + var indicesIndex = 2; + for (var i = 2; i < numberOfVertices; ++i) { + indices[indicesIndex++] = i - 1; + indices[indicesIndex++] = i; + } + + indices[indicesIndex++] = numberOfVertices - 1; + indices[indicesIndex] = 0; + + geometry.indices = indices; + geometry.primitiveType = PrimitiveType.LINES; + return geometry; + } + + function indexPrimitive(geometry) { + switch (geometry.primitiveType) { + case PrimitiveType.TRIANGLE_FAN: + return indexTriangleFan(geometry); + case PrimitiveType.TRIANGLE_STRIP: + return indexTriangleStrip(geometry); + case PrimitiveType.TRIANGLES: + return indexTriangles(geometry); + case PrimitiveType.LINE_STRIP: + return indexLineStrip(geometry); + case PrimitiveType.LINE_LOOP: + return indexLineLoop(geometry); + case PrimitiveType.LINES: + return indexLines(geometry); + } + + return geometry; + } + + function offsetPointFromXZPlane(p, isBehind) { + if (Math.abs(p.y) < CesiumMath.EPSILON11){ + if (isBehind) { + p.y = -CesiumMath.EPSILON11; + } else { + p.y = CesiumMath.EPSILON11; + } + } + } + + var c3 = new Cartesian3(); + function getXZIntersectionOffsetPoints(p, p1, u1, v1) { + Cartesian3.add(p, Cartesian3.multiplyByScalar(Cartesian3.subtract(p1, p, c3), p.y/(p.y-p1.y), c3), u1); + Cartesian3.clone(u1, v1); + offsetPointFromXZPlane(u1, true); + offsetPointFromXZPlane(v1, false); + } + + var u1 = new Cartesian3(); + var u2 = new Cartesian3(); + var q1 = new Cartesian3(); + var q2 = new Cartesian3(); + + var splitTriangleResult = { + positions : new Array(7), + indices : new Array(3 * 3) + }; + + function splitTriangle(p0, p1, p2) { + // In WGS84 coordinates, for a triangle approximately on the + // ellipsoid to cross the IDL, first it needs to be on the + // negative side of the plane x = 0. + if ((p0.x >= 0.0) || (p1.x >= 0.0) || (p2.x >= 0.0)) { + return undefined; + } + + var p0Behind = p0.y < 0.0; + var p1Behind = p1.y < 0.0; + var p2Behind = p2.y < 0.0; + + offsetPointFromXZPlane(p0, p0Behind); + offsetPointFromXZPlane(p1, p1Behind); + offsetPointFromXZPlane(p2, p2Behind); + + var numBehind = 0; + numBehind += p0Behind ? 1 : 0; + numBehind += p1Behind ? 1 : 0; + numBehind += p2Behind ? 1 : 0; + + var indices = splitTriangleResult.indices; + + if (numBehind === 1) { + indices[1] = 3; + indices[2] = 4; + indices[5] = 6; + indices[7] = 6; + indices[8] = 5; + + if (p0Behind) { + getXZIntersectionOffsetPoints(p0, p1, u1, q1); + getXZIntersectionOffsetPoints(p0, p2, u2, q2); + + indices[0] = 0; + indices[3] = 1; + indices[4] = 2; + indices[6] = 1; + } else if (p1Behind) { + getXZIntersectionOffsetPoints(p1, p2, u1, q1); + getXZIntersectionOffsetPoints(p1, p0, u2, q2); + + indices[0] = 1; + indices[3] = 2; + indices[4] = 0; + indices[6] = 2; + } else if (p2Behind) { + getXZIntersectionOffsetPoints(p2, p0, u1, q1); + getXZIntersectionOffsetPoints(p2, p1, u2, q2); + + indices[0] = 2; + indices[3] = 0; + indices[4] = 1; + indices[6] = 0; + } + } else if (numBehind === 2) { + indices[2] = 4; + indices[4] = 4; + indices[5] = 3; + indices[7] = 5; + indices[8] = 6; + + if (!p0Behind) { + getXZIntersectionOffsetPoints(p0, p1, u1, q1); + getXZIntersectionOffsetPoints(p0, p2, u2, q2); + + indices[0] = 1; + indices[1] = 2; + indices[3] = 1; + indices[6] = 0; + } else if (!p1Behind) { + getXZIntersectionOffsetPoints(p1, p2, u1, q1); + getXZIntersectionOffsetPoints(p1, p0, u2, q2); + + indices[0] = 2; + indices[1] = 0; + indices[3] = 2; + indices[6] = 1; + } else if (!p2Behind) { + getXZIntersectionOffsetPoints(p2, p0, u1, q1); + getXZIntersectionOffsetPoints(p2, p1, u2, q2); + + indices[0] = 0; + indices[1] = 1; + indices[3] = 0; + indices[6] = 2; + } + } + + var positions = splitTriangleResult.positions; + positions[0] = p0; + positions[1] = p1; + positions[2] = p2; + splitTriangleResult.length = 3; + + if (numBehind === 1 || numBehind === 2) { + positions[3] = u1; + positions[4] = u2; + positions[5] = q1; + positions[6] = q2; + splitTriangleResult.length = 7; + } + + return splitTriangleResult; + } + + function computeTriangleAttributes(i0, i1, i2, dividedTriangle, normals, binormals, tangents, texCoords) { + if (!defined(normals) && !defined(binormals) && !defined(tangents) && !defined(texCoords)) { + return; + } + + var positions = dividedTriangle.positions; + var p0 = positions[0]; + var p1 = positions[1]; + var p2 = positions[2]; + + var n0, n1, n2; + var b0, b1, b2; + var t0, t1, t2; + var s0, s1, s2; + var v0, v1, v2; + var u0, u1, u2; + + if (defined(normals)) { + n0 = Cartesian3.fromArray(normals, i0 * 3); + n1 = Cartesian3.fromArray(normals, i1 * 3); + n2 = Cartesian3.fromArray(normals, i2 * 3); + } + + if (defined(binormals)) { + b0 = Cartesian3.fromArray(binormals, i0 * 3); + b1 = Cartesian3.fromArray(binormals, i1 * 3); + b2 = Cartesian3.fromArray(binormals, i2 * 3); + } + + if (defined(tangents)) { + t0 = Cartesian3.fromArray(tangents, i0 * 3); + t1 = Cartesian3.fromArray(tangents, i1 * 3); + t2 = Cartesian3.fromArray(tangents, i2 * 3); + } + + if (defined(texCoords)) { + s0 = Cartesian2.fromArray(texCoords, i0 * 2); + s1 = Cartesian2.fromArray(texCoords, i1 * 2); + s2 = Cartesian2.fromArray(texCoords, i2 * 2); + } + + for (var i = 3; i < positions.length; ++i) { + var point = positions[i]; + var coords = barycentricCoordinates(point, p0, p1, p2); + + if (defined(normals)) { + v0 = Cartesian3.multiplyByScalar(n0, coords.x, v0); + v1 = Cartesian3.multiplyByScalar(n1, coords.y, v1); + v2 = Cartesian3.multiplyByScalar(n2, coords.z, v2); + + var normal = Cartesian3.add(v0, v1); + Cartesian3.add(normal, v2, normal); + Cartesian3.normalize(normal, normal); + + normals.push(normal.x, normal.y, normal.z); + } + + if (defined(binormals)) { + v0 = Cartesian3.multiplyByScalar(b0, coords.x, v0); + v1 = Cartesian3.multiplyByScalar(b1, coords.y, v1); + v2 = Cartesian3.multiplyByScalar(b2, coords.z, v2); + + var binormal = Cartesian3.add(v0, v1); + Cartesian3.add(binormal, v2, binormal); + Cartesian3.normalize(binormal, binormal); + + binormals.push(binormal.x, binormal.y, binormal.z); + } + + if (defined(tangents)) { + v0 = Cartesian3.multiplyByScalar(t0, coords.x, v0); + v1 = Cartesian3.multiplyByScalar(t1, coords.y, v1); + v2 = Cartesian3.multiplyByScalar(t2, coords.z, v2); + + var tangent = Cartesian3.add(v0, v1); + Cartesian3.add(tangent, v2, tangent); + Cartesian3.normalize(tangent, tangent); + + tangents.push(tangent.x, tangent.y, tangent.z); + } + + if (defined(texCoords)) { + u0 = Cartesian2.multiplyByScalar(s0, coords.x, u0); + u1 = Cartesian2.multiplyByScalar(s1, coords.y, u1); + u2 = Cartesian2.multiplyByScalar(s2, coords.z, u2); + + var texCoord = Cartesian2.add(u0, u1); + Cartesian2.add(texCoord, u2, texCoord); + + texCoords.push(texCoord.x, texCoord.y); + } + } + } + + function wrapLongitudeTriangles(geometry) { + var attributes = geometry.attributes; + var positions = attributes.position.values; + var normals = (defined(attributes.normal)) ? attributes.normal.values : undefined; + var binormals = (defined(attributes.binormal)) ? attributes.binormal.values : undefined; + var tangents = (defined(attributes.tangent)) ? attributes.tangent.values : undefined; + var texCoords = (defined(attributes.st)) ? attributes.st.values : undefined; + var indices = geometry.indices; + + var newPositions = Array.prototype.slice.call(positions, 0); + var newNormals = (defined(normals)) ? Array.prototype.slice.call(normals, 0) : undefined; + var newBinormals = (defined(binormals)) ? Array.prototype.slice.call(binormals, 0) : undefined; + var newTangents = (defined(tangents)) ? Array.prototype.slice.call(tangents, 0) : undefined; + var newTexCoords = (defined(texCoords)) ? Array.prototype.slice.call(texCoords, 0) : undefined; + var newIndices = []; + + var len = indices.length; + for (var i = 0; i < len; i += 3) { + var i0 = indices[i]; + var i1 = indices[i + 1]; + var i2 = indices[i + 2]; + + var p0 = Cartesian3.fromArray(positions, i0 * 3); + var p1 = Cartesian3.fromArray(positions, i1 * 3); + var p2 = Cartesian3.fromArray(positions, i2 * 3); + + var result = splitTriangle(p0, p1, p2); + if (defined(result)) { + newPositions[i0 * 3 + 1] = result.positions[0].y; + newPositions[i1 * 3 + 1] = result.positions[1].y; + newPositions[i2 * 3 + 1] = result.positions[2].y; + + if (result.length > 3) { + var positionsLength = newPositions.length / 3; + for(var j = 0; j < result.indices.length; ++j) { + var index = result.indices[j]; + if (index < 3) { + newIndices.push(indices[i + index]); + } else { + newIndices.push(index - 3 + positionsLength); + } + } + + for (var k = 3; k < result.positions.length; ++k) { + var position = result.positions[k]; + newPositions.push(position.x, position.y, position.z); + } + computeTriangleAttributes(i0, i1, i2, result, newNormals, newBinormals, newTangents, newTexCoords); + } else { + newIndices.push(i0, i1, i2); + } + } else { + newIndices.push(i0, i1, i2); + } + } + + geometry.attributes.position.values = new Float64Array(newPositions); + + if (defined(newNormals)) { + attributes.normal.values = ComponentDatatype.createTypedArray(attributes.normal.componentDatatype, newNormals); + } + + if (defined(newBinormals)) { + attributes.binormal.values = ComponentDatatype.createTypedArray(attributes.binormal.componentDatatype, newBinormals); + } + + if (defined(newTangents)) { + attributes.tangent.values = ComponentDatatype.createTypedArray(attributes.tangent.componentDatatype, newTangents); + } + + if (defined(newTexCoords)) { + attributes.st.values = ComponentDatatype.createTypedArray(attributes.st.componentDatatype, newTexCoords); + } + + var numberOfVertices = Geometry.computeNumberOfVertices(geometry); + geometry.indices = IndexDatatype.createTypedArray(numberOfVertices, newIndices); + } + + function wrapLongitudeLines(geometry) { + var attributes = geometry.attributes; + var positions = attributes.position.values; + var indices = geometry.indices; + + var newPositions = Array.prototype.slice.call(positions, 0); + var newIndices = []; + + var xzPlane = Plane.fromPointNormal(Cartesian3.ZERO, Cartesian3.UNIT_Y); + + var length = indices.length; + for ( var i = 0; i < length; i += 2) { + var i0 = indices[i]; + var i1 = indices[i + 1]; + + var prev = Cartesian3.fromArray(positions, i0 * 3); + var cur = Cartesian3.fromArray(positions, i1 * 3); + + if (Math.abs(prev.y) < CesiumMath.EPSILON6){ + if (prev.y < 0.0) { + prev.y = -CesiumMath.EPSILON6; + } else { + prev.y = CesiumMath.EPSILON6; + } + + newPositions[i0 * 3 + 1] = prev.y; + } + + if (Math.abs(cur.y) < CesiumMath.EPSILON6){ + if (cur.y < 0.0) { + cur.y = -CesiumMath.EPSILON6; + } else { + cur.y = CesiumMath.EPSILON6; + } + + newPositions[i1 * 3 + 1] = cur.y; + } + + newIndices.push(i0); + + // intersects the IDL if either endpoint is on the negative side of the yz-plane + if (prev.x < 0.0 || cur.x < 0.0) { + // and intersects the xz-plane + var intersection = IntersectionTests.lineSegmentPlane(prev, cur, xzPlane); + if (defined(intersection)) { + // move point on the xz-plane slightly away from the plane + var offset = Cartesian3.multiplyByScalar(Cartesian3.UNIT_Y, 5.0 * CesiumMath.EPSILON9); + if (prev.y < 0.0) { + Cartesian3.negate(offset, offset); + } + + var index = newPositions.length / 3; + newIndices.push(index, index + 1); + + var offsetPoint = Cartesian3.add(intersection, offset); + newPositions.push(offsetPoint.x, offsetPoint.y, offsetPoint.z); + + Cartesian3.negate(offset, offset); + Cartesian3.add(intersection, offset, offsetPoint); + newPositions.push(offsetPoint.x, offsetPoint.y, offsetPoint.z); + } + } + + newIndices.push(i1); + } + + geometry.attributes.position.values = new Float64Array(newPositions); + var numberOfVertices = Geometry.computeNumberOfVertices(geometry); + geometry.indices = IndexDatatype.createTypedArray(numberOfVertices, newIndices); + } + + /** + * Splits the geometry's primitives, by introducing new vertices and indices,that + * intersect the International Date Line so that no primitives cross longitude + * -180/180 degrees. This is not required for 3D drawing, but is required for + * correcting drawing in 2D and Columbus view. + * + * @param {Geometry} geometry The geometry to modify. + * + * @returns {Geometry} The modified <code>geometry</code> argument, with it's primitives split at the International Date Line. + * + * @exception {DeveloperError} geometry is required. + * + * @example + * geometry = GeometryPipeline.wrapLongitude(geometry); + */ + GeometryPipeline.wrapLongitude = function(geometry) { + if (!defined(geometry)) { + throw new DeveloperError('geometry is required.'); + } + + var boundingSphere = geometry.boundingSphere; + if (defined(boundingSphere)) { + var minX = boundingSphere.center.x - boundingSphere.radius; + if (minX > 0 || BoundingSphere.intersect(boundingSphere, Cartesian4.UNIT_Y) !== Intersect.INTERSECTING) { + return geometry; + } + } + + indexPrimitive(geometry); + if (geometry.primitiveType === PrimitiveType.TRIANGLES) { + wrapLongitudeTriangles(geometry); + } else if (geometry.primitiveType === PrimitiveType.LINES) { + wrapLongitudeLines(geometry); + } + + return geometry; + }; + + return GeometryPipeline; +}); + +/*global define*/ +define('Scene/PrimitivePipeline',[ + '../Core/defined', + '../Core/defaultValue', + '../Core/Color', + '../Core/ComponentDatatype', + '../Core/DeveloperError', + '../Core/FeatureDetection', + '../Core/Geometry', + '../Core/GeometryAttribute', + '../Core/GeometryPipeline', + '../Core/Matrix4' + ], function( + defined, + defaultValue, + Color, + ComponentDatatype, + DeveloperError, + FeatureDetection, + Geometry, + GeometryAttribute, + GeometryPipeline, + Matrix4) { + "use strict"; + + // Bail out if the browser doesn't support typed arrays, to prevent the setup function + // from failing, since we won't be able to create a WebGL context anyway. + if (!FeatureDetection.supportsTypedArrays()) { + return {}; + } + + function transformToWorldCoordinates(instances, primitiveModelMatrix, allow3DOnly) { + var toWorld = !allow3DOnly; + var length = instances.length; + var i; + + if (!toWorld && (length > 1)) { + var modelMatrix = instances[0].modelMatrix; + + for (i = 1; i < length; ++i) { + if (!Matrix4.equals(modelMatrix, instances[i].modelMatrix)) { + toWorld = true; + break; + } + } + } + + if (toWorld) { + for (i = 0; i < length; ++i) { + GeometryPipeline.transformToWorldCoordinates(instances[i]); + } + } else { + // Leave geometry in local coordinate system; auto update model-matrix. + Matrix4.clone(instances[0].modelMatrix, primitiveModelMatrix); + } + } + + function addPickColorAttribute(instances, pickIds) { + var length = instances.length; + + for (var i = 0; i < length; ++i) { + var instance = instances[i]; + var geometry = instance.geometry; + var attributes = geometry.attributes; + var positionAttr = attributes.position; + var numberOfComponents = 4 * (positionAttr.values.length / positionAttr.componentsPerAttribute); + + attributes.pickColor = new GeometryAttribute({ + componentDatatype : ComponentDatatype.UNSIGNED_BYTE, + componentsPerAttribute : 4, + normalize : true, + values : new Uint8Array(numberOfComponents) + }); + + var pickColor = pickIds[i]; + var red = Color.floatToByte(pickColor.red); + var green = Color.floatToByte(pickColor.green); + var blue = Color.floatToByte(pickColor.blue); + var alpha = Color.floatToByte(pickColor.alpha); + var values = attributes.pickColor.values; + + for (var j = 0; j < numberOfComponents; j += 4) { + values[j] = red; + values[j + 1] = green; + values[j + 2] = blue; + values[j + 3] = alpha; + } + } + } + + function getCommonPerInstanceAttributeNames(instances) { + var length = instances.length; + + var attributesInAllInstances = []; + var attributes0 = instances[0].attributes; + var name; + + for (name in attributes0) { + if (attributes0.hasOwnProperty(name)) { + var attribute = attributes0[name]; + var inAllInstances = true; + + // Does this same attribute exist in all instances? + for (var i = 1; i < length; ++i) { + var otherAttribute = instances[i].attributes[name]; + + if (!defined(otherAttribute) || + (attribute.componentDatatype.value !== otherAttribute.componentDatatype.value) || + (attribute.componentsPerAttribute !== otherAttribute.componentsPerAttribute) || + (attribute.normalize !== otherAttribute.normalize)) { + + inAllInstances = false; + break; + } + } + + if (inAllInstances) { + attributesInAllInstances.push(name); + } + } + } + + return attributesInAllInstances; + } + + function addPerInstanceAttributes(instances, names) { + var length = instances.length; + for (var i = 0; i < length; ++i) { + var instance = instances[i]; + var instanceAttributes = instance.attributes; + var geometry = instance.geometry; + var numberOfVertices = Geometry.computeNumberOfVertices(geometry); + + var namesLength = names.length; + for (var j = 0; j < namesLength; ++j) { + var name = names[j]; + var attribute = instanceAttributes[name]; + var componentDatatype = attribute.componentDatatype; + var value = attribute.value; + var componentsPerAttribute = value.length; + + var buffer = ComponentDatatype.createTypedArray(componentDatatype, numberOfVertices * componentsPerAttribute); + for (var k = 0; k < numberOfVertices; ++k) { + buffer.set(value, k * componentsPerAttribute); + } + + geometry.attributes[name] = new GeometryAttribute({ + componentDatatype : componentDatatype, + componentsPerAttribute : componentsPerAttribute, + normalize : attribute.normalize, + values : buffer + }); + } + } + } + + function geometryPipeline(parameters) { + var instances = parameters.instances; + var pickIds = parameters.pickIds; + var projection = parameters.projection; + var uintIndexSupport = parameters.elementIndexUintSupported; + var allow3DOnly = parameters.allow3DOnly; + var allowPicking = parameters.allowPicking; + var vertexCacheOptimize = parameters.vertexCacheOptimize; + var modelMatrix = parameters.modelMatrix; + + var i; + var length = instances.length; + var primitiveType = instances[0].geometry.primitiveType; + for (i = 1; i < length; ++i) { + if (instances[i].geometry.primitiveType !== primitiveType) { + throw new DeveloperError('All instance geometries must have the same primitiveType.'); + } + } + + // Unify to world coordinates before combining. + transformToWorldCoordinates(instances, modelMatrix, allow3DOnly); + + // Clip to IDL + if (!allow3DOnly) { + for (i = 0; i < length; ++i) { + GeometryPipeline.wrapLongitude(instances[i].geometry); + } + } + + // Add pickColor attribute for picking individual instances + if (allowPicking) { + addPickColorAttribute(instances, pickIds); + } + + // add attributes to the geometry for each per-instance attribute + var perInstanceAttributeNames = getCommonPerInstanceAttributeNames(instances); + addPerInstanceAttributes(instances, perInstanceAttributeNames); + + // Optimize for vertex shader caches + if (vertexCacheOptimize) { + for (i = 0; i < length; ++i) { + GeometryPipeline.reorderForPostVertexCache(instances[i].geometry); + GeometryPipeline.reorderForPreVertexCache(instances[i].geometry); + } + } + + // Combine into single geometry for better rendering performance. + var geometry = GeometryPipeline.combine(instances); + + // Split positions for GPU RTE + var attributes = geometry.attributes; + var name; + if (!allow3DOnly) { + for (name in attributes) { + if (attributes.hasOwnProperty(name) && attributes[name].componentDatatype.value === ComponentDatatype.DOUBLE.value) { + var name3D = name + '3D'; + var name2D = name + '2D'; + + // Compute 2D positions + GeometryPipeline.projectTo2D(geometry, name, name3D, name2D, projection); + + GeometryPipeline.encodeAttribute(geometry, name3D, name3D + 'High', name3D + 'Low'); + GeometryPipeline.encodeAttribute(geometry, name2D, name2D + 'High', name2D + 'Low'); + } + } + } else { + for (name in attributes) { + if (attributes.hasOwnProperty(name) && attributes[name].componentDatatype.value === ComponentDatatype.DOUBLE.value) { + GeometryPipeline.encodeAttribute(geometry, name, name + '3DHigh', name + '3DLow'); + } + } + } + + if (!uintIndexSupport) { + // Break into multiple geometries to fit within unsigned short indices if needed + return GeometryPipeline.fitToUnsignedShortIndices(geometry); + } + + // Unsigned int indices are supported. No need to break into multiple geometries. + return [geometry]; + } + + function createPerInstanceVAAttributes(geometry, attributeIndices, names) { + var vaAttributes = []; + var attributes = geometry.attributes; + + var length = names.length; + for (var i = 0; i < length; ++i) { + var name = names[i]; + var attribute = attributes[name]; + + var componentDatatype = attribute.componentDatatype; + if (componentDatatype.value === ComponentDatatype.DOUBLE.value) { + componentDatatype = ComponentDatatype.FLOAT; + } + + var typedArray = ComponentDatatype.createTypedArray(componentDatatype, attribute.values); + vaAttributes.push({ + index : attributeIndices[name], + componentDatatype : componentDatatype, + componentsPerAttribute : attribute.componentsPerAttribute, + normalize : attribute.normalize, + values : typedArray + }); + + delete attributes[name]; + } + + return vaAttributes; + } + + function computePerInstanceAttributeIndices(instances, vertexArrays, attributeIndices) { + var indices = []; + + var names = getCommonPerInstanceAttributeNames(instances); + var length = instances.length; + var offsets = {}; + var vaIndices = {}; + + for (var i = 0; i < length; ++i) { + var instance = instances[i]; + var numberOfVertices = Geometry.computeNumberOfVertices(instance.geometry); + + var namesLength = names.length; + for (var j = 0; j < namesLength; ++j) { + var name = names[j]; + var index = attributeIndices[name]; + + var tempVertexCount = numberOfVertices; + while (tempVertexCount > 0) { + var vaIndex = defaultValue(vaIndices[name], 0); + var va = vertexArrays[vaIndex]; + var vaLength = va.length; + + var attribute; + for (var k = 0; k < vaLength; ++k) { + attribute = va[k]; + if (attribute.index === index) { + break; + } + } + + if (!defined(indices[i])) { + indices[i] = {}; + } + + if (!defined(indices[i][name])) { + indices[i][name] = { + dirty : false, + value : instance.attributes[name].value, + indices : [] + }; + } + + var size = attribute.values.length / attribute.componentsPerAttribute; + var offset = defaultValue(offsets[name], 0); + + var count; + if (offset + tempVertexCount < size) { + count = tempVertexCount; + indices[i][name].indices.push({ + attribute : attribute, + offset : offset, + count : count + }); + offsets[name] = offset + tempVertexCount; + } else { + count = size - offset; + indices[i][name].indices.push({ + attribute : attribute, + offset : offset, + count : count + }); + offsets[name] = 0; + vaIndices[name] = vaIndex + 1; + } + + tempVertexCount -= count; + } + } + } + + return indices; + } + + /** + * @private + */ + var PrimitivePipeline = {}; + + /** + * @private + */ + PrimitivePipeline.combineGeometry = function(parameters) { + var clonedParameters = { + instances : parameters.instances, + pickIds : parameters.pickIds, + ellipsoid : parameters.ellipsoid, + projection : parameters.projection, + elementIndexUintSupported : parameters.elementIndexUintSupported, + allow3DOnly : parameters.allow3DOnly, + allowPicking : parameters.allowPicking, + vertexCacheOptimize : parameters.vertexCacheOptimize, + modelMatrix : Matrix4.clone(parameters.modelMatrix) + }; + var geometries = geometryPipeline(clonedParameters); + var attributeIndices = GeometryPipeline.createAttributeIndices(geometries[0]); + + var instances = clonedParameters.instances; + var perInstanceAttributeNames = getCommonPerInstanceAttributeNames(instances); + + var perInstanceAttributes = []; + var length = geometries.length; + for (var i = 0; i < length; ++i) { + var geometry = geometries[i]; + perInstanceAttributes.push(createPerInstanceVAAttributes(geometry, attributeIndices, perInstanceAttributeNames)); + } + + var indices = computePerInstanceAttributeIndices(instances, perInstanceAttributes, attributeIndices); + + return { + geometries : geometries, + modelMatrix : clonedParameters.modelMatrix, + attributeIndices : attributeIndices, + vaAttributes : perInstanceAttributes, + vaAttributeIndices : indices + }; + }; + + /* + * The below functions are needed when transferring typed arrays to/from web + * workers. This is a workaround for: + * + * https://bugzilla.mozilla.org/show_bug.cgi?id=841904 + */ + + function stupefyTypedArray(typedArray) { + return { + type : typedArray.constructor.name, + buffer : typedArray.buffer + }; + } + + var typedArrayMap = { + Int8Array : Int8Array, + Uint8Array : Uint8Array, + Int16Array : Int16Array, + Uint16Array : Uint16Array, + Int32Array : Int32Array, + Uint32Array : Uint32Array, + Float32Array : Float32Array, + Float64Array : Float64Array + }; + + function unStupefyTypedArray(typedArray) { + return new typedArrayMap[typedArray.type](typedArray.buffer); + } + + /** + * @private + */ + PrimitivePipeline.transferGeometry = function(geometry, transferableObjects) { + var typedArray; + var attributes = geometry.attributes; + for (var name in attributes) { + if (attributes.hasOwnProperty(name) && + defined(attributes[name]) && + defined(attributes[name].values)) { + typedArray = attributes[name].values; + + if (transferableObjects.indexOf(attributes[name].values.buffer) < 0) { + transferableObjects.push(typedArray.buffer); + } + + if (!defined(typedArray.type)) { + attributes[name].values = stupefyTypedArray(typedArray); + } + } + } + + if (defined(geometry.indices)) { + typedArray = geometry.indices; + transferableObjects.push(typedArray.buffer); + + if (!defined(typedArray.type)) { + geometry.indices = stupefyTypedArray(geometry.indices); + } + } + }; + + /** + * @private + */ + PrimitivePipeline.transferGeometries = function(geometries, transferableObjects) { + var length = geometries.length; + for (var i = 0; i < length; ++i) { + PrimitivePipeline.transferGeometry(geometries[i], transferableObjects); + } + }; + + /** + * @private + */ + PrimitivePipeline.transferPerInstanceAttributes = function(perInstanceAttributes, transferableObjects) { + var length = perInstanceAttributes.length; + for (var i = 0; i < length; ++i) { + var vaAttributes = perInstanceAttributes[i]; + var vaLength = vaAttributes.length; + for (var j = 0; j < vaLength; ++j) { + var typedArray = vaAttributes[j].values; + transferableObjects.push(typedArray.buffer); + vaAttributes[j].values = stupefyTypedArray(typedArray); + } + } + }; + + /** + * @private + */ + PrimitivePipeline.transferInstances = function(instances, transferableObjects) { + var length = instances.length; + for (var i = 0; i < length; ++i) { + var instance = instances[i]; + PrimitivePipeline.transferGeometry(instance.geometry, transferableObjects); + } + }; + + /** + * @private + */ + PrimitivePipeline.receiveGeometry = function(geometry) { + var attributes = geometry.attributes; + for (var name in attributes) { + if (attributes.hasOwnProperty(name) && + defined(attributes[name]) && + defined(attributes[name].values)) { + attributes[name].values = unStupefyTypedArray(attributes[name].values); + } + } + + if (defined(geometry.indices)) { + geometry.indices = unStupefyTypedArray(geometry.indices); + } + }; + + /** + * @private + */ + PrimitivePipeline.receiveGeometries = function(geometries) { + var length = geometries.length; + for (var i = 0; i < length; ++i) { + PrimitivePipeline.receiveGeometry(geometries[i]); + } + }; + + /** + * @private + */ + PrimitivePipeline.receivePerInstanceAttributes = function(perInstanceAttributes) { + var length = perInstanceAttributes.length; + for (var i = 0; i < length; ++i) { + var vaAttributes = perInstanceAttributes[i]; + var vaLength = vaAttributes.length; + for (var j = 0; j < vaLength; ++j) { + vaAttributes[j].values = unStupefyTypedArray(vaAttributes[j].values); + } + } + }; + + /** + * @private + */ + PrimitivePipeline.receiveInstances = function(instances) { + var length = instances.length; + for (var i = 0; i < length; ++i) { + var instance = instances[i]; + PrimitivePipeline.receiveGeometry(instance.geometry); + } + }; + + return PrimitivePipeline; +}); + +/*global define*/ +define('Workers/createTaskProcessorWorker',[ + '../Core/defaultValue', + '../Core/defined' + ], function( + defaultValue, + defined) { + "use strict"; + + /** + * Creates an adapter function to allow a calculation function to operate as a Web Worker, + * paired with TaskProcessor, to receive tasks and return results. + * + * @exports createTaskProcessorWorker + * + * @param {Function} workerFunction A function that takes as input two arguments: + * a parameters object, and an array into which transferable result objects can be pushed, + * and returns as output a result object. + * @returns {Function} An adapter function that handles the interaction with TaskProcessor, + * specifically, task ID management and posting a response message containing the result. + * + * @example + * function doCalculation(parameters, transferableObjects) { + * // calculate some result using the inputs in parameters + * return result; + * } + * + * return createTaskProcessorWorker(doCalculation); + * // the resulting function is compatible with TaskProcessor + * + * @see TaskProcessor + * @see <a href='http://www.w3.org/TR/workers/'>Web Workers</a> + * @see <a href='http://www.w3.org/TR/html5/common-dom-interfaces.html#transferable-objects'>Transferable objects</a> + */ + var createTaskProcessorWorker = function(workerFunction) { + var postMessage; + var transferableObjects = []; + var responseMessage = { + id : undefined, + result : undefined, + error : undefined + }; + + return function(event) { + /*global self*/ + var data = event.data; + + transferableObjects.length = 0; + responseMessage.id = data.id; + responseMessage.error = undefined; + responseMessage.result = undefined; + + try { + responseMessage.result = workerFunction(data.parameters, transferableObjects); + } catch (e) { + responseMessage.error = e; + } + + if (!defined(postMessage)) { + postMessage = defaultValue(self.webkitPostMessage, self.postMessage); + } + + try { + postMessage(responseMessage, transferableObjects); + } catch (e) { + // something went wrong trying to post the message, post a simpler + // error that we can be sure will be cloneable + responseMessage.result = undefined; + responseMessage.error = 'postMessage failed with error: ' + e + '\n with responseMessage: ' + JSON.stringify(responseMessage); + postMessage(responseMessage); + } + }; + }; + + return createTaskProcessorWorker; +}); +/*global define*/ +define('Workers/createExtentOutlineGeometry',[ + '../Core/ExtentOutlineGeometry', + '../Core/Ellipsoid', + '../Core/Extent', + '../Scene/PrimitivePipeline', + './createTaskProcessorWorker' + ], function( ExtentOutlineGeometry, Ellipsoid, Extent, PrimitivePipeline, createTaskProcessorWorker) { @@ -21,5 +18863,6 @@ }; } return createTaskProcessorWorker(createExtentOutlineGeometry); }); +}()); \ No newline at end of file